/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

More work on roundtrip push support.

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