/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.264.10 by Jelmer Vernooij
Yield inventory entries.
1
# Copyright (C) 2008-2011 Jelmer Vernooij <jelmer@samba.org>
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
17
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
18
"""An adapter between a Git index and a Bazaar Working Tree"""
19
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
20
0.200.385 by Jelmer Vernooij
Cope with removed files.
21
from cStringIO import (
22
    StringIO,
23
    )
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
24
from collections import defaultdict
0.239.4 by Jelmer Vernooij
Cope with nonexistent files and directories in get_file_sha1.
25
import errno
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
26
from dulwich.index import (
27
    Index,
28
    )
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
29
from dulwich.object_store import (
30
    tree_lookup_path,
31
    )
0.200.383 by Jelmer Vernooij
Simplify, support rewriting index based on inventory.
32
from dulwich.objects import (
33
    Blob,
0.200.948 by Jelmer Vernooij
Cope with empty inventories.
34
    ZERO_SHA,
35
    )
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
36
import os
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
37
import posix
0.264.10 by Jelmer Vernooij
Yield inventory entries.
38
import posixpath
0.200.384 by Jelmer Vernooij
Fix reading of inventory from index.
39
import stat
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
40
import sys
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
41
42
from bzrlib import (
0.200.382 by Jelmer Vernooij
Support flushing index.
43
    errors,
0.262.1 by Jelmer Vernooij
Fix WorkingTree.conflicts().
44
    conflicts as _mod_conflicts,
0.200.409 by Jelmer Vernooij
Support parsing .gitignore.
45
    ignores,
0.264.10 by Jelmer Vernooij
Yield inventory entries.
46
    inventory,
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
47
    lockable_files,
48
    lockdir,
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
49
    osutils,
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
50
    trace,
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
51
    transport,
0.200.519 by Jelmer Vernooij
Move imports down, might not be available in older bzr-git versions.
52
    tree,
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
53
    workingtree,
54
    )
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
55
from bzrlib.decorators import (
56
    needs_read_lock,
57
    )
58
59
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
60
from bzrlib.plugins.git.dir import (
61
    LocalGitDir,
62
    )
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
63
from bzrlib.plugins.git.tree import (
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
64
    changes_from_git_changes,
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
65
    tree_delta_from_git_changes,
66
    )
0.200.971 by Chadrik
Fix 'bzr status' after 'bzr add' in native git working trees.
67
from bzrlib.plugins.git.mapping import (
68
    GitFileIdMap,
0.264.10 by Jelmer Vernooij
Yield inventory entries.
69
    mode_kind,
0.200.971 by Chadrik
Fix 'bzr status' after 'bzr add' in native git working trees.
70
    )
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
71
0.200.409 by Jelmer Vernooij
Support parsing .gitignore.
72
IGNORE_FILENAME = ".gitignore"
73
74
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
75
class GitWorkingTree(workingtree.WorkingTree):
76
    """A Git working tree."""
77
0.200.803 by Jelmer Vernooij
Default to non-bare repositories when initializing a control directory.
78
    def __init__(self, bzrdir, repo, branch, index):
0.200.379 by Jelmer Vernooij
Re-enable working tree support.
79
        self.basedir = bzrdir.root_transport.local_abspath('.')
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
80
        self.bzrdir = bzrdir
81
        self.repository = repo
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
82
        self.store = self.repository._git.object_store
0.200.384 by Jelmer Vernooij
Fix reading of inventory from index.
83
        self.mapping = self.repository.get_mapping()
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
84
        self._branch = branch
85
        self._transport = bzrdir.transport
86
0.246.3 by Jelmer Vernooij
Simplify call to abspath, without involving private variables.
87
        self.controldir = self.bzrdir.transport.local_abspath('bzr')
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
88
89
        try:
90
            os.makedirs(self.controldir)
91
            os.makedirs(os.path.join(self.controldir, 'lock'))
92
        except OSError:
93
            pass
94
95
        self._control_files = lockable_files.LockableFiles(
96
            transport.get_transport(self.controldir), 'lock', lockdir.LockDir)
97
        self._format = GitWorkingTreeFormat()
0.200.803 by Jelmer Vernooij
Default to non-bare repositories when initializing a control directory.
98
        self.index = index
0.200.239 by Jelmer Vernooij
Provide views.
99
        self.views = self._make_views()
0.200.1173 by Jelmer Vernooij
Provide GitWorkingTree._rules_searcher.
100
        self._rules_searcher = None
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
101
        self._detect_case_handling()
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
102
        self._reset_data()
103
        self._fileid_map = self._basis_fileid_map.copy()
0.200.173 by Jelmer Vernooij
Merge changes, open index.
104
0.200.1220 by Jelmer Vernooij
Support set_parent_trees.
105
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
106
        self.set_parent_ids([p for p, t in parents_list])
107
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
108
    def _index_add_entry(self, path, file_id, kind):
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
109
        assert isinstance(path, basestring)
110
        assert type(file_id) == str
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
111
        if kind == "directory":
112
            # Git indexes don't contain directories
113
            return
114
        if kind == "file":
115
            blob = Blob()
116
            try:
117
                file, stat_val = self.get_file_with_stat(file_id, path)
118
            except (errors.NoSuchFile, IOError):
119
                # TODO: Rather than come up with something here, use the old index
120
                file = StringIO()
121
                from posix import stat_result
122
                stat_val = stat_result((stat.S_IFREG | 0644, 0, 0, 0, 0, 0, 0, 0, 0, 0))
123
            blob.set_raw_string(file.read())
124
        elif kind == "symlink":
125
            blob = Blob()
126
            try:
127
                stat_val = os.lstat(self.abspath(path))
128
            except (errors.NoSuchFile, OSError):
129
                # TODO: Rather than come up with something here, use the 
130
                # old index
131
                from posix import stat_result
132
                stat_val = stat_result((stat.S_IFLNK, 0, 0, 0, 0, 0, 0, 0, 0, 0))
133
            blob.set_raw_string(self.get_symlink_target(file_id).encode("utf-8"))
134
        else:
135
            raise AssertionError("unknown kind '%s'" % kind)
136
        # Add object to the repository if it didn't exist yet
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
137
        if not blob.id in self.store:
138
            self.store.add_object(blob)
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
139
        # Add an entry to the index or update the existing entry
140
        flags = 0 # FIXME
141
        self.index[path.encode("utf-8")] = (stat_val.st_ctime,
142
                stat_val.st_mtime, stat_val.st_dev, stat_val.st_ino,
143
                stat_val.st_mode, stat_val.st_uid, stat_val.st_gid,
144
                stat_val.st_size, blob.id, flags)
145
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
146
    def _unversion_path(self, path):
147
        encoded_path = path.encode("utf-8")
148
        try:
149
            del self.index[encoded_path]
150
        except KeyError:
151
            # A directory, perhaps?
152
            for p in list(self.index):
153
                if p.startswith(encoded_path+"/"):
154
                    del self.index[p]
155
0.200.1192 by Jelmer Vernooij
Implement path2id.
156
    def unversion(self, file_ids):
157
        for file_id in file_ids:
158
            path = self.id2path(file_id)
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
159
            self._unversion_path(path)
160
161
    def remove(self, files, verbose=False, to_file=None, keep_files=True,
162
        force=False):
163
        """Remove nominated files from the working tree metadata.
164
165
        :param files: File paths relative to the basedir.
166
        :param keep_files: If true, the files will also be kept.
167
        :param force: Delete files and directories, even if they are changed
168
            and even if the directories are not empty.
169
        """
170
        all_files = set() # specified and nested files 
171
172
        if isinstance(files, basestring):
173
            files = [files]
174
175
        if to_file is None:
176
            to_file = sys.stdout
177
178
        files = list(all_files)
179
180
        if len(files) == 0:
181
            return # nothing to do
182
183
        # Sort needed to first handle directory content before the directory
184
        files.sort(reverse=True)
185
186
        def backup(file_to_backup):
187
            abs_path = self.abspath(file_to_backup)
188
            backup_name = self.bzrdir._available_backup_name(file_to_backup)
189
            osutils.rename(abs_path, self.abspath(backup_name))
190
            return "removed %s (but kept a copy: %s)" % (
191
                file_to_backup, backup_name)
192
193
        for f in files:
194
            fid = self.path2id(f)
195
            if not fid:
196
                message = "%s is not versioned." % (f,)
197
            else:
198
                abs_path = self.abspath(f)
199
                if verbose:
200
                    # having removed it, it must be either ignored or unknown
201
                    if self.is_ignored(f):
202
                        new_status = 'I'
203
                    else:
204
                        new_status = '?'
205
                    # XXX: Really should be a more abstract reporter interface
206
                    kind_ch = osutils.kind_marker(self.kind(fid))
207
                    to_file.write(new_status + '       ' + f + kind_ch + '\n')
208
                # Unversion file
209
                # FIXME: _unversion_path() is O(size-of-index) for directories
210
                self._unversion_path(f)
211
                message = "removed %s" % (f,)
212
                if osutils.lexists(abs_path):
213
                    if (osutils.isdir(abs_path) and
214
                        len(os.listdir(abs_path)) > 0):
215
                        if force:
216
                            osutils.rmtree(abs_path)
217
                            message = "deleted %s" % (f,)
218
                        else:
219
                            message = backup(f)
220
                    else:
221
                        if not keep_files:
222
                            osutils.delete_any(abs_path)
223
                            message = "deleted %s" % (f,)
224
225
            # print only one message (if any) per file.
226
            if message is not None:
227
                trace.note(message)
0.200.1192 by Jelmer Vernooij
Implement path2id.
228
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
229
    def _add(self, files, ids, kinds):
230
        for (path, file_id, kind) in zip(files, ids, kinds):
0.200.1201 by Jelmer Vernooij
Implement _set_root_id.
231
            if file_id is not None:
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
232
                self._fileid_map.set_file_id(path.encode("utf-8"), file_id)
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
233
            else:
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
234
                file_id = self._fileid_map.lookup_file_id(path.encode("utf-8"))
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
235
            self._index_add_entry(path, file_id, kind)
0.200.1201 by Jelmer Vernooij
Implement _set_root_id.
236
237
    def _set_root_id(self, file_id):
238
        self._fileid_map.set_file_id("", file_id)
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
239
0.200.1193 by Jelmer Vernooij
Implement GitWorkingTree.{move,rename_one}.
240
    def move(self, from_paths, to_dir=None, after=False):
241
        rename_tuples = []
242
        to_abs = self.abspath(to_dir)
243
        if not os.path.isdir(to_abs):
244
            raise errors.BzrMoveFailedError('', to_dir,
245
                errors.NotADirectory(to_abs))
246
247
        for from_rel in from_paths:
248
            from_tail = os.path.split(from_rel)[-1]
249
            to_rel = os.path.join(to_dir, from_tail)
250
            self.rename_one(from_rel, to_rel, after=after)
251
            rename_tuples.append((from_rel, to_rel))
252
        return rename_tuples
253
254
    def rename_one(self, from_rel, to_rel, after=False):
255
        if not after:
256
            os.rename(self.abspath(from_rel), self.abspath(to_rel))
0.200.1203 by Jelmer Vernooij
Fix per_workingtree.test_rename_one.TestRenameOne.test_rename_after_non_existant_non_ascii
257
        from_path = from_rel.encode("utf-8")
258
        to_path = to_rel.encode("utf-8")
259
        if not self.has_filename(to_rel):
260
            raise errors.BzrMoveFailedError(from_rel, to_rel,
261
                errors.NoSuchFile(to_rel))
262
        if not from_path in self.index:
263
            raise errors.BzrMoveFailedError(from_rel, to_rel,
264
                errors.NotVersionedError(path=from_rel))
265
        self.index[to_path] = self.index[from_path]
266
        del self.index[from_path]
0.200.1193 by Jelmer Vernooij
Implement GitWorkingTree.{move,rename_one}.
267
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
268
    def get_root_id(self):
0.200.1192 by Jelmer Vernooij
Implement path2id.
269
        return self.path2id("")
270
271
    @needs_read_lock
272
    def path2id(self, path):
273
        return self._fileid_map.lookup_file_id(path.encode("utf-8"))
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
274
0.200.605 by Jelmer Vernooij
Ignore directories in WorkingTree.extras().
275
    def extras(self):
276
        """Yield all unversioned files in this WorkingTree.
277
        """
0.200.615 by Jelmer Vernooij
Optimize WorkingTree.extras().
278
        for (dirpath, dirnames, filenames) in os.walk(self.basedir):
279
            if self.bzrdir.is_control_filename(dirpath[len(self.basedir):].strip("/")):
0.200.605 by Jelmer Vernooij
Ignore directories in WorkingTree.extras().
280
                continue
0.200.615 by Jelmer Vernooij
Optimize WorkingTree.extras().
281
            for filename in filenames:
282
                relpath = os.path.join(dirpath[len(self.basedir):].strip("/"), filename)
283
                if not relpath in self.index:
0.200.605 by Jelmer Vernooij
Ignore directories in WorkingTree.extras().
284
                    yield relpath
285
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
286
    def unlock(self):
0.200.224 by Jelmer Vernooij
Fix working tree locking.
287
        # non-implementation specific cleanup
288
        self._cleanup()
289
290
        # reverse order of locking.
291
        try:
292
            return self._control_files.unlock()
293
        finally:
294
            self.branch.unlock()
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
295
0.200.382 by Jelmer Vernooij
Support flushing index.
296
    def flush(self):
297
        # TODO: Maybe this should only write on dirty ?
298
        if self._control_files._lock_mode != 'w':
299
            raise errors.NotWriteLocked(self)
0.200.385 by Jelmer Vernooij
Cope with removed files.
300
        self.index.write()
0.200.382 by Jelmer Vernooij
Support flushing index.
301
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
302
    def __iter__(self):
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
303
        for path in self.index:
0.200.1192 by Jelmer Vernooij
Implement path2id.
304
            yield self.path2id(path)
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
305
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
306
    def has_or_had_id(self, file_id):
307
        if self.has_id(file_id):
308
            return True
309
        if self.had_id(file_id):
310
            return True
311
        return False
312
313
    def had_id(self, file_id):
314
        path = self._basis_fileid_map.lookup_file_id(file_id)
315
        try:
316
            head = self.repository._git.head()
317
        except KeyError:
318
            # Assume no if basis is not accessible
319
            return False
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
320
        if head == ZERO_SHA:
321
            return False
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
322
        root_tree = self.store[head].tree
323
        try:
324
            tree_lookup_path(self.store.__getitem__, root_tree, path)
325
        except KeyError:
326
            return False
327
        else:
328
            return True
329
0.200.1198 by Jelmer Vernooij
Implement GitWorkingTree.has_id.
330
    def has_id(self, file_id):
331
        try:
332
            self.id2path(file_id)
333
        except errors.NoSuchId:
334
            return False
335
        else:
336
            return True
337
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
338
    def id2path(self, file_id):
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
339
        if type(file_id) != str:
340
            raise AssertionError
341
        path = self._fileid_map.lookup_path(file_id)
0.200.1213 by Jelmer Vernooij
Add note about directories.
342
        # FIXME: What about directories?
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
343
        if path in self.index:
344
            return path
0.200.1214 by Jelmer Vernooij
Set container when raising NoSuchId.
345
        raise errors.NoSuchId(self, file_id)
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
346
0.200.1200 by Jelmer Vernooij
Support GitWorkingTree.get_file_mtime.
347
    def get_file_mtime(self, file_id, path=None):
348
        """See Tree.get_file_mtime."""
349
        if not path:
350
            path = self.id2path(file_id)
351
        return os.lstat(self.abspath(path)).st_mtime
352
0.200.409 by Jelmer Vernooij
Support parsing .gitignore.
353
    def get_ignore_list(self):
354
        ignoreset = getattr(self, '_ignoreset', None)
355
        if ignoreset is not None:
356
            return ignoreset
357
358
        ignore_globs = set()
359
        ignore_globs.update(ignores.get_runtime_ignores())
360
        ignore_globs.update(ignores.get_user_ignores())
361
        if self.has_filename(IGNORE_FILENAME):
362
            f = self.get_file_byname(IGNORE_FILENAME)
363
            try:
0.200.1198 by Jelmer Vernooij
Implement GitWorkingTree.has_id.
364
                # FIXME: Parse git file format, rather than assuming it's
365
                # the same as for bzr's native formats.
0.200.409 by Jelmer Vernooij
Support parsing .gitignore.
366
                ignore_globs.update(ignores.parse_ignore_file(f))
367
            finally:
368
                f.close()
369
        self._ignoreset = ignore_globs
370
        return ignore_globs
371
0.200.508 by Jelmer Vernooij
Skip inventory caching bits.
372
    def set_last_revision(self, revid):
373
        self._change_last_revision(revid)
374
0.200.379 by Jelmer Vernooij
Re-enable working tree support.
375
    def _reset_data(self):
0.248.3 by Jelmer Vernooij
Handle working trees without valid HEAD branch.
376
        try:
377
            head = self.repository._git.head()
378
        except KeyError, name:
379
            raise errors.NotBranchError("branch %s at %s" % (name, self.repository.base))
0.200.948 by Jelmer Vernooij
Cope with empty inventories.
380
        if head == ZERO_SHA:
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
381
            self._basis_fileid_map = GitFileIdMap({}, self.mapping)
0.200.948 by Jelmer Vernooij
Cope with empty inventories.
382
        else:
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
383
            self._basis_fileid_map = self.mapping.get_fileid_map(self.store.__getitem__,
384
                self.store[head].tree)
0.200.379 by Jelmer Vernooij
Re-enable working tree support.
385
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
386
    @needs_read_lock
387
    def get_file_sha1(self, file_id, path=None, stat_value=None):
388
        if not path:
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
389
            path = self.id2path(file_id)
0.239.4 by Jelmer Vernooij
Cope with nonexistent files and directories in get_file_sha1.
390
        try:
391
            return osutils.sha_file_by_name(self.abspath(path).encode(osutils._fs_enc))
392
        except OSError, (num, msg):
393
            if num in (errno.EISDIR, errno.ENOENT):
394
                return None
395
            raise
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
396
0.200.610 by Jelmer Vernooij
Support retrieving basis tree properly.
397
    def revision_tree(self, revid):
398
        return self.repository.revision_tree(revid)
399
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
400
    def _get_dir_ie(self, path, parent_id):
0.200.1192 by Jelmer Vernooij
Implement path2id.
401
        file_id = self.path2id(path)
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
402
        return inventory.InventoryDirectory(file_id,
0.200.1190 by Jelmer Vernooij
Fix get_symlink_target call.
403
            posixpath.basename(path).strip("/"), parent_id)
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
404
405
    def _add_missing_parent_ids(self, path, dir_ids):
406
        if path in dir_ids:
407
            return []
408
        parent = posixpath.dirname(path).strip("/")
409
        ret = self._add_missing_parent_ids(parent, dir_ids)
410
        parent_id = dir_ids[parent]
411
        ie = self._get_dir_ie(path, parent_id)
412
        dir_ids[path] = ie.file_id
413
        ret.append((path, ie))
414
        return ret
415
0.264.10 by Jelmer Vernooij
Yield inventory entries.
416
    def _get_file_ie(self, path, value, parent_id):
0.200.1192 by Jelmer Vernooij
Implement path2id.
417
        assert isinstance(path, unicode)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
418
        assert isinstance(value, tuple) and len(value) == 10
419
        (ctime, mtime, dev, ino, mode, uid, gid, size, sha, flags) = value
0.200.1192 by Jelmer Vernooij
Implement path2id.
420
        file_id = self.path2id(path)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
421
        if type(file_id) != str:
422
            raise AssertionError
423
        kind = mode_kind(mode)
424
        ie = inventory.entry_factory[kind](file_id,
0.200.1192 by Jelmer Vernooij
Implement path2id.
425
            posixpath.basename(path), parent_id)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
426
        if kind == 'symlink':
0.200.1190 by Jelmer Vernooij
Fix get_symlink_target call.
427
            ie.symlink_target = self.get_symlink_target(file_id)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
428
        else:
429
            data = self.get_file_text(file_id, path)
430
            ie.text_sha1 = osutils.sha_string(data)
431
            ie.text_size = len(data)
432
            ie.executable = self.is_executable(file_id, path)
433
        ie.revision = None
434
        return ie
435
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
436
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
437
        mode = stat_result.st_mode
438
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
439
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
440
    def stored_kind(self, file_id, path=None):
441
        if path is None:
442
            path = self.id2path(file_id)
443
        head = self.repository._git.head()
444
        if head == ZERO_SHA:
445
            raise errors.NoSuchId(self, file_id)
446
        root_tree = self.store[head].tree
447
        (mode, hexsha) = tree_lookup_path(self.store.__getitem__, root_tree, path)
448
        return mode_kind(mode)
449
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
450
    if not osutils.supports_executable():
451
        def is_executable(self, file_id, path=None):
452
            basis_tree = self.basis_tree()
453
            if file_id in basis_tree:
454
                return basis_tree.is_executable(file_id)
455
            # Default to not executable
456
            return False
457
    else:
458
        def is_executable(self, file_id, path=None):
459
            if not path:
460
                path = self.id2path(file_id)
461
            mode = os.lstat(self.abspath(path)).st_mode
462
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
463
464
        _is_executable_from_path_and_stat = \
465
            _is_executable_from_path_and_stat_from_stat
466
0.264.10 by Jelmer Vernooij
Yield inventory entries.
467
    def list_files(self, include_root=False, from_dir=None, recursive=True):
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
468
        # FIXME: Yield non-versioned files
469
        # FIXME: support from_dir
470
        # FIXME: Support recursive
471
        dir_ids = {}
0.200.1192 by Jelmer Vernooij
Implement path2id.
472
        root_ie = self._get_dir_ie(u"", None)
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
473
        if include_root and not from_dir:
474
            yield "", "V", root_ie.kind, root_ie.file_id, root_ie
0.200.1192 by Jelmer Vernooij
Implement path2id.
475
        dir_ids[u""] = root_ie.file_id
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
476
        for path, value in self.index.iteritems():
0.200.1192 by Jelmer Vernooij
Implement path2id.
477
            path = path.decode("utf-8")
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
478
            parent = posixpath.dirname(path).strip("/")
479
            for dir_path, dir_ie in self._add_missing_parent_ids(parent, dir_ids):
480
                yield dir_path, "V", dir_ie.kind, dir_ie.file_id, dir_ie
481
            ie = self._get_file_ie(path, value, dir_ids[parent])
482
            yield path, "V", ie.kind, ie.file_id, ie
0.264.10 by Jelmer Vernooij
Yield inventory entries.
483
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
484
    def all_file_ids(self):
485
        ids = {u"": self.path2id("")}
486
        for path in self.index:
487
            path = path.decode("utf-8")
488
            parent = posixpath.dirname(path).strip("/")
489
            for e in self._add_missing_parent_ids(parent, ids):
490
                pass
491
            ids[path] = self.path2id(path)
492
        return set(ids.values())
493
0.264.9 by Jelmer Vernooij
Implement basic GitWorkingTree.iter_entries_by_dir.
494
    def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
495
        # FIXME: Support specific_file_ids
496
        # FIXME: Is return order correct?
497
        if specific_file_ids is not None:
498
            raise NotImplementedError(self.iter_entries_by_dir)
0.200.1192 by Jelmer Vernooij
Implement path2id.
499
        root_ie = self._get_dir_ie(u"", None)
500
        yield u"", root_ie
501
        dir_ids = {u"": root_ie.file_id}
0.264.10 by Jelmer Vernooij
Yield inventory entries.
502
        for path, value in self.index.iteritems():
0.200.1192 by Jelmer Vernooij
Implement path2id.
503
            path = path.decode("utf-8")
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
504
            parent = posixpath.dirname(path).strip("/")
0.200.1207 by Jelmer Vernooij
Fix some formatting.
505
            for (dir_path, dir_ie) in self._add_missing_parent_ids(parent,
506
                    dir_ids):
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
507
                yield dir_path, dir_ie
0.200.1192 by Jelmer Vernooij
Implement path2id.
508
            parent_id = self.path2id(parent)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
509
            yield path, self._get_file_ie(path, value, parent_id)
0.264.9 by Jelmer Vernooij
Implement basic GitWorkingTree.iter_entries_by_dir.
510
0.200.619 by Jelmer Vernooij
Provide dummy WorkingTree.conflicts() implementation rather than spending a lot of time not finding any conflicts.
511
    @needs_read_lock
512
    def conflicts(self):
513
        # FIXME:
0.262.1 by Jelmer Vernooij
Fix WorkingTree.conflicts().
514
        return _mod_conflicts.ConflictList()
0.200.619 by Jelmer Vernooij
Provide dummy WorkingTree.conflicts() implementation rather than spending a lot of time not finding any conflicts.
515
0.200.1193 by Jelmer Vernooij
Implement GitWorkingTree.{move,rename_one}.
516
    def update_basis_by_delta(self, new_revid, delta):
0.200.1196 by Jelmer Vernooij
Implement GitWorkingTree.update_basis_by_delta.
517
        # The index just contains content, which won't have changed.
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
518
        self._reset_data()
0.200.1193 by Jelmer Vernooij
Implement GitWorkingTree.{move,rename_one}.
519
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
520
    def _walkdirs(self, prefix=""):
521
        if prefix != "":
522
            prefix += "/"
523
        per_dir = defaultdict(list)
524
        for path, value in self.index.iteritems():
525
            if not path.startswith(prefix):
526
                continue
527
            (dirname, child_name) = posixpath.split(path)
528
            dirname = dirname.decode("utf-8")
529
            dir_file_id = self.path2id(dirname)
530
            assert isinstance(value, tuple) and len(value) == 10
531
            (ctime, mtime, dev, ino, mode, uid, gid, size, sha, flags) = value
0.200.1211 by Jelmer Vernooij
Fix statresult.
532
            stat_result = posix.stat_result((mode, ino,
533
                    dev, 1, uid, gid, size,
534
                    0, mtime, ctime))
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
535
            per_dir[(dirname, dir_file_id)].append(
536
                (path.decode("utf-8"), child_name.decode("utf-8"),
537
                mode_kind(mode), stat_result,
538
                self.path2id(path.decode("utf-8")),
539
                mode_kind(mode)))
540
        return per_dir.iteritems()
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
541
542
class GitWorkingTreeFormat(workingtree.WorkingTreeFormat):
543
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
544
    _tree_class = GitWorkingTree
545
0.200.656 by Jelmer Vernooij
Implement GitWorkingTreeFormat._matchingbzrdir.
546
    @property
547
    def _matchingbzrdir(self):
0.200.1140 by Jelmer Vernooij
Update now that the control dir formats are no longer in __init__.
548
        from bzrlib.plugins.git.dir import LocalGitControlDirFormat
0.200.1013 by Jelmer Vernooij
More renames.
549
        return LocalGitControlDirFormat()
0.200.656 by Jelmer Vernooij
Implement GitWorkingTreeFormat._matchingbzrdir.
550
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
551
    def get_format_description(self):
552
        return "Git Working Tree"
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
553
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
554
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
555
                   accelerator_tree=None, hardlink=False):
556
        """See WorkingTreeFormat.initialize()."""
557
        if not isinstance(a_bzrdir, LocalGitDir):
558
            raise errors.IncompatibleFormat(self, a_bzrdir)
559
        index = Index(a_bzrdir.root_transport.local_abspath(".git/index"))
560
        index.write()
561
        return GitWorkingTree(a_bzrdir, a_bzrdir.open_repository(),
562
            a_bzrdir.open_branch(), index)
563
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
564
565
class InterIndexGitTree(tree.InterTree):
566
    """InterTree that works between a Git revision tree and an index."""
567
568
    def __init__(self, source, target):
569
        super(InterIndexGitTree, self).__init__(source, target)
570
        self._index = target.index
571
572
    @classmethod
573
    def is_compatible(cls, source, target):
574
        from bzrlib.plugins.git.repository import GitRevisionTree
575
        return (isinstance(source, GitRevisionTree) and 
576
                isinstance(target, GitWorkingTree))
577
578
    def compare(self, want_unchanged=False, specific_files=None,
579
                extra_trees=None, require_versioned=False, include_root=False,
580
                want_unversioned=False):
581
        changes = self._index.changes_from_tree(
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
582
            self.source.store, self.source.tree, 
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
583
            want_unchanged=want_unchanged)
0.200.920 by Jelmer Vernooij
Fix some more tests.
584
        source_fileid_map = self.source.mapping.get_fileid_map(
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
585
            self.source.store.__getitem__,
0.252.43 by Jelmer Vernooij
Some refactoring, support proper file ids in revision deltas.
586
            self.source.tree)
0.200.922 by Jelmer Vernooij
Fix remaining test.
587
        if self.target.mapping.BZR_FILE_IDS_FILE is not None:
0.200.1030 by Jelmer Vernooij
More work on supporting roundtripping push.
588
            file_id = self.target.path2id(
589
                self.target.mapping.BZR_FILE_IDS_FILE)
590
            if file_id is None:
0.200.922 by Jelmer Vernooij
Fix remaining test.
591
                target_fileid_map = {}
592
            else:
0.200.1207 by Jelmer Vernooij
Fix some formatting.
593
                target_fileid_map = self.target.mapping.import_fileid_map(
594
                    Blob.from_string(self.target.get_file_text(file_id)))
0.200.922 by Jelmer Vernooij
Fix remaining test.
595
        else:
596
            target_fileid_map = {}
0.200.1207 by Jelmer Vernooij
Fix some formatting.
597
        target_fileid_map = GitFileIdMap(target_fileid_map,
598
                self.target.mapping)
0.200.1030 by Jelmer Vernooij
More work on supporting roundtripping push.
599
        ret = tree_delta_from_git_changes(changes, self.target.mapping,
0.252.43 by Jelmer Vernooij
Some refactoring, support proper file ids in revision deltas.
600
            (source_fileid_map, target_fileid_map),
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
601
            specific_file=specific_files, require_versioned=require_versioned)
602
        if want_unversioned:
603
            for e in self.target.extras():
0.200.1207 by Jelmer Vernooij
Fix some formatting.
604
                ret.unversioned.append((e, None,
605
                    osutils.file_kind(self.target.abspath(e))))
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
606
        return ret
607
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
608
    def iter_changes(self, include_unchanged=False, specific_files=None,
0.200.1207 by Jelmer Vernooij
Fix some formatting.
609
        pb=None, extra_trees=[], require_versioned=True,
610
        want_unversioned=False):
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
611
        changes = self._index.changes_from_tree(
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
612
            self.source.store, self.source.tree,
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
613
            want_unchanged=include_unchanged)
614
        # FIXME: Handle want_unversioned
0.200.1179 by Jelmer Vernooij
Avoid using verifiers for natively imported revisions, save a lot of time.
615
        return changes_from_git_changes(changes, self.target.mapping,
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
616
            specific_file=specific_files)
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
617
0.200.1179 by Jelmer Vernooij
Avoid using verifiers for natively imported revisions, save a lot of time.
618
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
619
tree.InterTree.register_optimiser(InterIndexGitTree)