/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

Handle empty metadata.

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,
26
23
    urlutils,
 
24
    version_info as bzrlib_version,
27
25
    )
28
26
 
29
 
lazy_import(globals(), """
30
 
from bzrlib.lockable_files import TransportLock
 
27
LockWarner = getattr(lockable_files, "_LockWarner", None)
 
28
 
31
29
from bzrlib.plugins.git import (
32
 
    errors,
33
 
    branch,
34
 
    repository,
35
 
    workingtree,
 
30
    LocalGitControlDirFormat,
36
31
    )
37
 
""")
38
 
 
 
32
try:
 
33
    from bzrlib.controldir import (
 
34
        ControlDir,
 
35
        format_registry,
 
36
        )
 
37
except ImportError:
 
38
    # bzr < 2.3
 
39
    from bzrlib.bzrdir import (
 
40
        BzrDir,
 
41
        format_registry,
 
42
        )
 
43
    ControlDir = BzrDir
39
44
 
40
45
 
41
46
class GitLock(object):
56
61
    def validate_token(self, token):
57
62
        pass
58
63
 
 
64
    def break_lock(self):
 
65
        pass
 
66
 
59
67
 
60
68
class GitLockableFiles(lockable_files.LockableFiles):
61
69
    """Git specific lockable files abstraction."""
64
72
        self._lock = lock
65
73
        self._transaction = None
66
74
        self._lock_mode = None
67
 
        self._lock_count = 0
68
75
        self._transport = transport
69
 
 
70
 
 
71
 
class GitDir(bzrdir.BzrDir):
 
76
        if LockWarner is None:
 
77
            # Bzr 1.13
 
78
            self._lock_count = 0
 
79
        else:
 
80
            self._lock_warner = LockWarner(repr(self))
 
81
 
 
82
 
 
83
class GitDir(ControlDir):
72
84
    """An adapter to the '.git' dir used by git."""
73
85
 
74
86
    def is_supported(self):
75
87
        return True
76
88
 
 
89
    def can_convert_format(self):
 
90
        return False
 
91
 
77
92
    def cloning_metadir(self, stacked=False):
78
 
        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")
 
93
        return format_registry.make_bzrdir("default")
 
94
 
 
95
    def _branch_name_to_ref(self, name):
 
96
        raise NotImplementedError(self._branch_name_to_ref)
 
97
 
 
98
    if bzrlib_version >= (2, 2):
 
99
        def open_branch(self, name=None, unsupported=False, 
 
100
            ignore_fallbacks=None):
 
101
            return self._open_branch(name=name,
 
102
                ignore_fallbacks=ignore_fallbacks, unsupported=unsupported)
 
103
    else:
 
104
        def open_branch(self, ignore_fallbacks=None, unsupported=False):
 
105
            return self._open_branch(name=None,
 
106
                ignore_fallbacks=ignore_fallbacks, unsupported=unsupported)
82
107
 
83
108
 
84
109
class LocalGitDir(GitDir):
85
110
    """An adapter to the '.git' dir used by git."""
86
111
 
87
 
    _gitrepository_class = repository.LocalGitRepository
 
112
    def _get_gitrepository_class(self):
 
113
        from bzrlib.plugins.git.repository import LocalGitRepository
 
114
        return LocalGitRepository
 
115
 
 
116
    _gitrepository_class = property(_get_gitrepository_class)
 
117
 
 
118
    @property
 
119
    def user_transport(self):
 
120
        return self.root_transport
 
121
 
 
122
    @property
 
123
    def control_transport(self):
 
124
        return self.transport
88
125
 
89
126
    def __init__(self, transport, lockfiles, gitrepo, format):
90
127
        self._format = format
91
128
        self.root_transport = transport
 
129
        self._mode_check_done = False
92
130
        self._git = gitrepo
93
131
        if gitrepo.bare:
94
132
            self.transport = transport
95
133
        else:
96
134
            self.transport = transport.clone('.git')
97
135
        self._lockfiles = lockfiles
98
 
 
99
 
    def get_branch_transport(self, branch_format):
 
136
        self._mode_check_done = None
 
137
 
 
138
    def _branch_name_to_ref(self, name):
 
139
        from bzrlib.plugins.git.refs import branch_name_to_ref
 
140
        ref = branch_name_to_ref(name, None)
 
141
        if ref == "HEAD":
 
142
            from dulwich.repo import SYMREF
 
143
            refcontents = self._git.refs.read_ref(ref)
 
144
            if refcontents.startswith(SYMREF):
 
145
                ref = refcontents[len(SYMREF):]
 
146
        return ref
 
147
 
 
148
    def is_control_filename(self, filename):
 
149
        return filename == '.git' or filename.startswith('.git/')
 
150
 
 
151
    def get_branch_transport(self, branch_format, name=None):
100
152
        if branch_format is None:
101
153
            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):
 
154
        if isinstance(branch_format, LocalGitControlDirFormat):
 
155
            return self.transport
 
156
        raise bzr_errors.IncompatibleFormat(branch_format, self._format)
 
157
 
 
158
    def get_repository_transport(self, format):
 
159
        if format is None:
 
160
            return self.transport
 
161
        if isinstance(format, LocalGitControlDirFormat):
 
162
            return self.transport
 
163
        raise bzr_errors.IncompatibleFormat(format, self._format)
 
164
 
 
165
    def get_workingtree_transport(self, format):
 
166
        if format is None:
 
167
            return self.transport
 
168
        if isinstance(format, LocalGitControlDirFormat):
 
169
            return self.transport
 
170
        raise bzr_errors.IncompatibleFormat(format, self._format)
 
171
 
 
172
    def _open_branch(self, name=None, ignore_fallbacks=None, unsupported=False):
110
173
        """'create' a branch for this dir."""
111
174
        repo = self.open_repository()
112
 
        return branch.LocalGitBranch(self, repo, "HEAD", repo._git.head(), self._lockfiles)
 
175
        from bzrlib.plugins.git.branch import LocalGitBranch
 
176
        return LocalGitBranch(self, repo, self._branch_name_to_ref(name),
 
177
            self._lockfiles)
 
178
 
 
179
    def destroy_branch(self, name=None):
 
180
        refname = self._branch_name_to_ref(name)
 
181
        if not refname in self._git.refs:
 
182
            raise bzr_errors.NotBranchError(self.root_transport.base,
 
183
                    bzrdir=self)
 
184
        del self._git.refs[refname]
 
185
 
 
186
    def destroy_repository(self):
 
187
        raise bzr_errors.UnsupportedOperation(self.destroy_repository, self)
 
188
 
 
189
    def destroy_workingtree(self):
 
190
        raise bzr_errors.UnsupportedOperation(self.destroy_workingtree, self)
 
191
 
 
192
    def needs_format_conversion(self, format=None):
 
193
        return not isinstance(self._format, format.__class__)
 
194
 
 
195
    def list_branches(self):
 
196
        ret = []
 
197
        for name in self._git.get_refs():
 
198
            if name.startswith("refs/heads/"):
 
199
                ret.append(self.open_branch(name=name))
 
200
        return ret
113
201
 
114
202
    def open_repository(self, shared=False):
115
203
        """'open' a repository for this dir."""
116
204
        return self._gitrepository_class(self, self._lockfiles)
117
205
 
118
206
    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())
 
207
        if not self._git.bare:
 
208
            from dulwich.errors import NoIndexPresent
 
209
            repo = self.open_repository()
 
210
            try:
 
211
                index = repo._git.open_index()
 
212
            except NoIndexPresent:
 
213
                pass
 
214
            else:
 
215
                from bzrlib.plugins.git.workingtree import GitWorkingTree
 
216
                try:
 
217
                    branch = self.open_branch()
 
218
                except bzr_errors.NotBranchError:
 
219
                    pass
 
220
                else:
 
221
                    return GitWorkingTree(self, repo, branch, index)
 
222
        loc = urlutils.unescape_for_display(self.root_transport.base, 'ascii')
 
223
        raise bzr_errors.NoWorkingTree(loc)
125
224
 
126
225
    def create_repository(self, shared=False):
127
226
        return self.open_repository()
128
227
 
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)
 
228
    def create_branch(self, name=None):
 
229
        refname = self._branch_name_to_ref(name)
 
230
        from dulwich.protocol import ZERO_SHA
 
231
        self._git.refs[refname or "HEAD"] = ZERO_SHA
 
232
        return self.open_branch(name)
 
233
 
 
234
    def backup_bzrdir(self):
 
235
        if self._git.bare:
 
236
            self.root_transport.copy_tree(".git", ".git.backup")
 
237
            return (self.root_transport.abspath(".git"),
 
238
                    self.root_transport.abspath(".git.backup"))
 
239
        else:
 
240
            raise bzr_errors.BzrError("Unable to backup bare repositories")
 
241
 
 
242
    def create_workingtree(self, revision_id=None, from_branch=None,
 
243
        accelerator_tree=None, hardlink=False):
 
244
        if self._git.bare:
 
245
            raise bzr_errors.BzrError("Can't create working tree in a bare repo")
 
246
        from dulwich.index import write_index
 
247
        from dulwich.pack import SHA1Writer
 
248
        f = open(self.transport.local_abspath("index"), 'w+')
 
249
        try:
 
250
            f = SHA1Writer(f)
 
251
            write_index(f, [])
 
252
        finally:
 
253
            f.close()
 
254
        return self.open_workingtree()
 
255
 
 
256
    def find_repository(self):
 
257
        """Find the repository that should be used.
 
258
 
 
259
        This does not require a branch as we use it to find the repo for
 
260
        new branches as well as to hook existing branches up to their
 
261
        repository.
 
262
        """
 
263
        return self.open_repository()
 
264
 
 
265
    def _find_creation_modes(self):
 
266
        """Determine the appropriate modes for files and directories.
 
267
 
 
268
        They're always set to be consistent with the base directory,
 
269
        assuming that this transport allows setting modes.
 
270
        """
 
271
        # TODO: Do we need or want an option (maybe a config setting) to turn
 
272
        # this off or override it for particular locations? -- mbp 20080512
 
273
        if self._mode_check_done:
 
274
            return
 
275
        self._mode_check_done = True
 
276
        try:
 
277
            st = self.transport.stat('.')
 
278
        except TransportNotPossible:
 
279
            self._dir_mode = None
 
280
            self._file_mode = None
 
281
        else:
 
282
            # Check the directory mode, but also make sure the created
 
283
            # directories and files are read-write for this user. This is
 
284
            # mostly a workaround for filesystems which lie about being able to
 
285
            # write to a directory (cygwin & win32)
 
286
            if (st.st_mode & 07777 == 00000):
 
287
                # FTP allows stat but does not return dir/file modes
 
288
                self._dir_mode = None
 
289
                self._file_mode = None
 
290
            else:
 
291
                self._dir_mode = (st.st_mode & 07777) | 00700
 
292
                # Remove the sticky and execute bits for files
 
293
                self._file_mode = self._dir_mode & ~07111
 
294
 
 
295
    def _get_file_mode(self):
 
296
        """Return Unix mode for newly created files, or None.
 
297
        """
 
298
        if not self._mode_check_done:
 
299
            self._find_creation_modes()
 
300
        return self._file_mode
 
301
 
 
302
    def _get_dir_mode(self):
 
303
        """Return Unix mode for newly created directories, or None.
 
304
        """
 
305
        if not self._mode_check_done:
 
306
            self._find_creation_modes()
 
307
        return self._dir_mode
 
308
 
251
309