/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.358.2 by Jelmer Vernooij
Refresh copyright headers, add my email.
1
# Copyright (C) 2009-2018 Jelmer Vernooij <jelmer@jelmer.uk>
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision 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
0.358.1 by Jelmer Vernooij
Fix FSF address.
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
16
17
18
"""Git Trees."""
19
0.200.1594 by Jelmer Vernooij
Use absolute_import everywhere.
20
from __future__ import absolute_import
21
0.360.2 by Jelmer Vernooij
Fix test.
22
import errno
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
23
from io import BytesIO
24
import os
25
26
from dulwich.index import (
27
    index_entry_from_stat,
28
    )
0.200.1780 by Jelmer Vernooij
Fix cross-object-store tree comparison.
29
from dulwich.object_store import (
30
    tree_lookup_path,
31
    OverlayObjectStore,
32
    )
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
33
from dulwich.objects import (
34
    Blob,
35
    Tree,
0.372.1 by Jelmer Vernooij
Fix some missing id handling.
36
    ZERO_SHA,
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
37
    )
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
38
import stat
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
39
import posixpath
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
40
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
41
from ... import (
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
42
    delta,
43
    errors,
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
44
    lock,
45
    mutabletree,
0.264.10 by Jelmer Vernooij
Yield inventory entries.
46
    osutils,
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
47
    revisiontree,
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
48
    trace,
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
49
    tree as _mod_tree,
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
50
    workingtree,
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
51
    )
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
52
from ...revision import NULL_REVISION
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
53
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
54
from .mapping import (
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
55
    mode_is_executable,
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
56
    mode_kind,
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
57
    GitFileIdMap,
0.287.2 by Jelmer Vernooij
Support empty trees in GitRevisionTree.
58
    default_mapping,
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
59
    )
60
61
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
62
class GitTreeDirectory(_mod_tree.TreeDirectory):
63
64
    __slots__ = ['file_id', 'name', 'parent_id', 'children', 'revision']
65
66
    def __init__(self, file_id, name, parent_id, revision=None):
67
        self.file_id = file_id
68
        self.name = name
69
        self.parent_id = parent_id
70
        # TODO(jelmer)
71
        self.children = {}
72
        self.revision = revision
73
74
    @property
75
    def kind(self):
76
        return 'directory'
77
78
    @property
79
    def executable(self):
80
        return False
81
82
    def copy(self):
83
        return self.__class__(
84
            self.file_id, self.name, self.parent_id, self.revision)
85
86
    def __repr__(self):
87
        return "%s(file_id=%r, name=%r, parent_id=%r, revision=%r)" % (
88
            self.__class__.__name__, self.file_id, self.name,
89
            self.parent_id, self.revision)
90
91
    def __eq__(self, other):
92
        return (self.kind == other.kind and
93
                self.file_id == other.file_id and
94
                self.name == other.name and
95
                self.parent_id == other.parent_id and
96
                self.revision == other.revision)
97
98
99
class GitTreeFile(_mod_tree.TreeFile):
100
101
    __slots__ = ['file_id', 'name', 'parent_id', 'text_size', 'text_sha1', 'revision',
102
                 'executable']
103
104
    def __init__(self, file_id, name, parent_id, revision=None):
105
        self.file_id = file_id
106
        self.name = name
107
        self.parent_id = parent_id
108
        self.revision = revision
109
        self.text_size = None
110
        self.text_sha1 = None
111
        self.executable = None
112
113
    @property
114
    def kind(self):
115
        return 'file'
116
117
    def __eq__(self, other):
118
        return (self.kind == other.kind and
119
                self.file_id == other.file_id and
120
                self.name == other.name and
121
                self.parent_id == other.parent_id and
122
                self.revision == other.revision and
123
                self.text_sha1 == other.text_sha1 and
124
                self.text_size == other.text_size and
125
                self.executable == other.executable)
126
0.365.2 by Jelmer Vernooij
Add eq/copy.
127
    def copy(self):
128
        ret = self.__class__(
129
                self.file_id, self.name, self.parent_id,
130
                self.revision)
131
        ret.text_sha1 = self.text_sha1
132
        ret.text_size = self.text_size
133
        ret.executable = self.executable
134
        return ret
135
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
136
137
class GitTreeSymlink(_mod_tree.TreeLink):
138
139
    __slots__ = ['file_id', 'name', 'parent_id', 'symlink_target', 'revision']
140
0.365.2 by Jelmer Vernooij
Add eq/copy.
141
    def __init__(self, file_id, name, parent_id, revision=None,
142
                 symlink_target=None):
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
143
        self.file_id = file_id
144
        self.name = name
145
        self.parent_id = parent_id
0.365.2 by Jelmer Vernooij
Add eq/copy.
146
        self.revision = revision
147
        self.symlink_target = symlink_target
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
148
149
    @property
150
    def kind(self):
151
        return 'symlink'
152
153
    @property
154
    def executable(self):
155
        return False
156
157
    @property
158
    def text_size(self):
159
        return None
160
0.365.2 by Jelmer Vernooij
Add eq/copy.
161
    def __eq__(self, other):
162
        return (self.kind == other.kind and
163
                self.file_id == other.file_id and
164
                self.name == other.name and
165
                self.parent_id == other.parent_id and
166
                self.revision == other.revision and
167
                self.symlink_target == other.symlink_target)
168
169
    def copy(self):
170
        return self.__class__(
171
                self.file_id, self.name, self.parent_id,
172
                self.revision, self.symlink_target)
173
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
174
175
entry_factory = {
176
    'directory': GitTreeDirectory,
177
    'file': GitTreeFile,
178
    'symlink': GitTreeSymlink,
179
    }
180
181
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
182
def ensure_normalized_path(path):
183
    """Check whether path is normalized.
184
185
    :raises InvalidNormalization: When path is not normalized, and cannot be
186
        accessed on this platform by the normalized path.
187
    :return: The NFC normalised version of path.
188
    """
189
    norm_path, can_access = osutils.normalized_filename(path)
190
    if norm_path != path:
191
        if can_access:
192
            return norm_path
193
        else:
194
            raise errors.InvalidNormalization(path)
195
    return path
196
197
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
198
class GitRevisionTree(revisiontree.RevisionTree):
0.200.959 by Jelmer Vernooij
Improve docstrings.
199
    """Revision tree implementation based on Git objects."""
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
200
201
    def __init__(self, repository, revision_id):
202
        self._revision_id = revision_id
203
        self._repository = repository
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
204
        self.store = repository._git.object_store
0.361.1 by Jelmer Vernooij
Don't use assert.
205
        if type(revision_id) is not str:
206
            raise TypeError(revision_id)
0.200.1283 by Jelmer Vernooij
Provide Repository.get_file_graph() and Tree.get_file_revision().
207
        self.commit_id, self.mapping = repository.lookup_bzr_revision_id(revision_id)
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
208
        if revision_id == NULL_REVISION:
209
            self.tree = None
0.287.2 by Jelmer Vernooij
Support empty trees in GitRevisionTree.
210
            self.mapping = default_mapping
211
            self._fileid_map = GitFileIdMap(
0.287.4 by Jelmer Vernooij
Fix GitRevisionTree behaviour for null:
212
                {},
0.287.2 by Jelmer Vernooij
Support empty trees in GitRevisionTree.
213
                default_mapping)
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
214
        else:
215
            try:
216
                commit = self.store[self.commit_id]
217
            except KeyError, r:
218
                raise errors.NoSuchRevision(repository, revision_id)
219
            self.tree = commit.tree
220
            self._fileid_map = self.mapping.get_fileid_map(self.store.__getitem__, self.tree)
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
221
0.349.1 by Jelmer Vernooij
Support supports_rename_tracking method.
222
    def supports_rename_tracking(self):
223
        return False
224
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
225
    def get_file_revision(self, path, file_id=None):
0.200.1283 by Jelmer Vernooij
Provide Repository.get_file_graph() and Tree.get_file_revision().
226
        change_scanner = self._repository._file_change_scanner
0.372.1 by Jelmer Vernooij
Fix some missing id handling.
227
        if self.commit_id == ZERO_SHA:
228
            return NULL_REVISION
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
229
        (path, commit_id) = change_scanner.find_last_change_revision(
230
            path.encode('utf-8'), self.commit_id)
0.200.1283 by Jelmer Vernooij
Provide Repository.get_file_graph() and Tree.get_file_revision().
231
        return self._repository.lookup_foreign_revision_id(commit_id, self.mapping)
232
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
233
    def get_file_mtime(self, path, file_id=None):
234
        revid = self.get_file_revision(path, file_id)
0.200.1283 by Jelmer Vernooij
Provide Repository.get_file_graph() and Tree.get_file_revision().
235
        try:
236
            rev = self._repository.get_revision(revid)
237
        except errors.NoSuchRevision:
238
            raise errors.FileTimestampUnavailable(path)
239
        return rev.timestamp
240
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
241
    def id2path(self, file_id):
0.200.1712 by Jelmer Vernooij
Add file_id prefix.
242
        try:
0.329.1 by Jelmer Vernooij
Check that path actually exists in Tree.id2path.
243
            path = self._fileid_map.lookup_path(file_id)
0.200.1712 by Jelmer Vernooij
Add file_id prefix.
244
        except ValueError:
0.200.1714 by Jelmer Vernooij
Fix NoSuchId raising.
245
            raise errors.NoSuchId(self, file_id)
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
246
        path = path.decode('utf-8')
0.372.1 by Jelmer Vernooij
Fix some missing id handling.
247
        if self.is_versioned(path):
0.329.1 by Jelmer Vernooij
Check that path actually exists in Tree.id2path.
248
            return path
249
        raise errors.NoSuchId(self, file_id)
250
251
    def is_versioned(self, path):
252
        return self.has_filename(path)
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
253
254
    def path2id(self, path):
0.200.1328 by Jelmer Vernooij
More test fixes.
255
        if self.mapping.is_special_file(path):
256
            return None
257
        return self._fileid_map.lookup_file_id(path.encode('utf-8'))
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
258
0.200.1569 by Jelmer Vernooij
Implement GitRevisionTree.all_file_ids().
259
    def all_file_ids(self):
260
        return set(self._fileid_map.all_file_ids())
261
0.200.1724 by Jelmer Vernooij
Add GitRevisionTree.all_versioned_paths implementation.
262
    def all_versioned_paths(self):
263
        ret = set()
264
        todo = set([('', self.tree)])
265
        while todo:
266
            (path, tree_id) = todo.pop()
0.287.2 by Jelmer Vernooij
Support empty trees in GitRevisionTree.
267
            if tree_id is None:
268
                continue
0.200.1724 by Jelmer Vernooij
Add GitRevisionTree.all_versioned_paths implementation.
269
            tree = self.store[tree_id]
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
270
            for name, mode, hexsha in tree.items():
0.200.1724 by Jelmer Vernooij
Add GitRevisionTree.all_versioned_paths implementation.
271
                subpath = posixpath.join(path, name)
272
                if stat.S_ISDIR(mode):
273
                    todo.add((subpath, hexsha))
274
                else:
275
                    ret.add(subpath)
276
        return ret
277
0.200.1204 by Jelmer Vernooij
Implement GitRevisionTree.get_root_id().
278
    def get_root_id(self):
0.378.1 by Jelmer Vernooij
Return None from Tree.get_root_id() when encountering an empty tree.
279
        if self.tree is None:
280
            return None
0.200.1204 by Jelmer Vernooij
Implement GitRevisionTree.get_root_id().
281
        return self.path2id("")
282
0.200.1208 by Jelmer Vernooij
Add GitWorkingTree.has_id and GitWorkingTree.has_or_had_id.
283
    def has_or_had_id(self, file_id):
0.372.1 by Jelmer Vernooij
Fix some missing id handling.
284
        try:
285
            path = self.id2path(file_id)
286
        except errors.NoSuchId:
287
            return False
288
        return True
0.200.1208 by Jelmer Vernooij
Add GitWorkingTree.has_id and GitWorkingTree.has_or_had_id.
289
290
    def has_id(self, file_id):
291
        try:
292
            path = self.id2path(file_id)
293
        except errors.NoSuchId:
294
            return False
295
        return self.has_filename(path)
296
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
297
    def is_executable(self, path, file_id=None):
0.200.1615 by Jelmer Vernooij
Implement GitRevisionTree.is_executable.:
298
        try:
299
            (mode, hexsha) = tree_lookup_path(self.store.__getitem__, self.tree,
300
                path)
301
        except KeyError:
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
302
            raise errors.NoSuchId(self, path)
0.200.1615 by Jelmer Vernooij
Implement GitRevisionTree.is_executable.:
303
        if mode is None:
304
            # the tree root is a directory
305
            return False
306
        return mode_is_executable(mode)
307
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
308
    def kind(self, path, file_id=None):
0.287.4 by Jelmer Vernooij
Fix GitRevisionTree behaviour for null:
309
        if self.tree is None:
0.287.5 by Jelmer Vernooij
Fix root handling.
310
            raise errors.NoSuchFile(path)
0.200.1283 by Jelmer Vernooij
Provide Repository.get_file_graph() and Tree.get_file_revision().
311
        try:
312
            (mode, hexsha) = tree_lookup_path(self.store.__getitem__, self.tree,
313
                path)
314
        except KeyError:
0.287.5 by Jelmer Vernooij
Fix root handling.
315
            raise errors.NoSuchFile(path)
0.200.1253 by Jelmer Vernooij
Fix Tree.kind(TREE_ROOT).
316
        if mode is None:
317
            # the tree root is a directory
318
            return "directory"
0.200.1241 by Jelmer Vernooij
Implement GitRevisionTree.kind.
319
        return mode_kind(mode)
320
0.200.1197 by Jelmer Vernooij
Implement GitRevisionTree.has_filename.
321
    def has_filename(self, path):
0.287.4 by Jelmer Vernooij
Fix GitRevisionTree behaviour for null:
322
        if self.tree is None:
323
            return False
0.200.1197 by Jelmer Vernooij
Implement GitRevisionTree.has_filename.
324
        try:
325
            tree_lookup_path(self.store.__getitem__, self.tree,
326
                path.encode("utf-8"))
327
        except KeyError:
328
            return False
329
        else:
330
            return True
331
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
332
    def list_files(self, include_root=False, from_dir=None, recursive=True):
333
        if from_dir is None:
0.200.1197 by Jelmer Vernooij
Implement GitRevisionTree.has_filename.
334
            from_dir = u""
0.287.2 by Jelmer Vernooij
Support empty trees in GitRevisionTree.
335
        if self.tree is None:
336
            return
0.200.1197 by Jelmer Vernooij
Implement GitRevisionTree.has_filename.
337
        (mode, hexsha) = tree_lookup_path(self.store.__getitem__, self.tree,
338
            from_dir.encode("utf-8"))
0.264.9 by Jelmer Vernooij
Implement basic GitWorkingTree.iter_entries_by_dir.
339
        if mode is None: # Root
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
340
            root_ie = self._get_dir_ie(b"", None)
0.264.9 by Jelmer Vernooij
Implement basic GitWorkingTree.iter_entries_by_dir.
341
        else:
0.264.10 by Jelmer Vernooij
Yield inventory entries.
342
            parent_path = posixpath.dirname(from_dir.encode("utf-8"))
0.200.1328 by Jelmer Vernooij
More test fixes.
343
            parent_id = self._fileid_map.lookup_file_id(parent_path)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
344
            if mode_kind(mode) == 'directory':
345
                root_ie = self._get_dir_ie(from_dir.encode("utf-8"), parent_id)
346
            else:
347
                root_ie = self._get_file_ie(from_dir.encode("utf-8"),
348
                    posixpath.basename(from_dir), mode, hexsha)
349
        if from_dir != "" or include_root:
350
            yield (from_dir, "V", root_ie.kind, root_ie.file_id, root_ie)
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
351
        todo = set()
0.264.10 by Jelmer Vernooij
Yield inventory entries.
352
        if root_ie.kind == 'directory':
353
            todo.add((from_dir.encode("utf-8"), hexsha, root_ie.file_id))
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
354
        while todo:
0.264.10 by Jelmer Vernooij
Yield inventory entries.
355
            (path, hexsha, parent_id) = todo.pop()
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
356
            tree = self.store[hexsha]
357
            for name, mode, hexsha in tree.iteritems():
0.200.1328 by Jelmer Vernooij
More test fixes.
358
                if self.mapping.is_special_file(name):
359
                    continue
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
360
                child_path = posixpath.join(path, name)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
361
                if stat.S_ISDIR(mode):
362
                    ie = self._get_dir_ie(child_path, parent_id)
363
                    if recursive:
364
                        todo.add((child_path, hexsha, ie.file_id))
365
                else:
366
                    ie = self._get_file_ie(child_path, name, mode, hexsha, parent_id)
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
367
                yield child_path.decode('utf-8'), "V", ie.kind, ie.file_id, ie
0.264.10 by Jelmer Vernooij
Yield inventory entries.
368
369
    def _get_file_ie(self, path, name, mode, hexsha, parent_id):
0.361.1 by Jelmer Vernooij
Don't use assert.
370
        if type(path) is not bytes:
371
            raise TypeError(path)
372
        if type(name) is not bytes:
373
            raise TypeError(name)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
374
        kind = mode_kind(mode)
0.200.1328 by Jelmer Vernooij
More test fixes.
375
        file_id = self._fileid_map.lookup_file_id(path)
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
376
        ie = entry_factory[kind](file_id, name.decode("utf-8"), parent_id)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
377
        if kind == 'symlink':
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
378
            ie.symlink_target = self.store[hexsha].data.decode('utf-8')
0.200.1401 by Jelmer Vernooij
Cope with submodules in working trees.
379
        elif kind == 'tree-reference':
380
            ie.reference_revision = self.mapping.revision_id_foreign_to_bzr(hexsha)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
381
        else:
382
            data = self.store[hexsha].data
383
            ie.text_sha1 = osutils.sha_string(data)
384
            ie.text_size = len(data)
385
            ie.executable = mode_is_executable(mode)
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
386
        ie.revision = self.get_file_revision(path.decode('utf-8'))
0.264.10 by Jelmer Vernooij
Yield inventory entries.
387
        return ie
388
389
    def _get_dir_ie(self, path, parent_id):
0.200.1328 by Jelmer Vernooij
More test fixes.
390
        file_id = self._fileid_map.lookup_file_id(path)
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
391
        ie = GitTreeDirectory(file_id,
0.264.10 by Jelmer Vernooij
Yield inventory entries.
392
            posixpath.basename(path).decode("utf-8"), parent_id)
0.360.4 by Jelmer Vernooij
Implement MemoryTree.rename_one, MemoryTree.mkdir.
393
        ie.revision = self.get_file_revision(path.decode('utf-8'))
0.287.6 by Jelmer Vernooij
Fix some more tests.
394
        return ie
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
395
0.200.1744 by Jelmer Vernooij
Implement GitRevisionTree.iter_children.
396
    def iter_children(self, file_id):
397
        path = self._fileid_map.lookup_path(file_id)
398
        mode, tree_sha = tree_lookup_path(self.store.__getitem__, self.tree, path)
399
        if stat.S_ISDIR(mode):
400
            for name, mode, hexsha  in self.store[tree_sha].iteritems():
401
                yield self._fileid_map.lookup_file_id(posixpath.join(path, name))
402
0.374.1 by Jelmer Vernooij
Implement GitRevisionTree.iter_child_entries.
403
    def iter_child_entries(self, path, file_id=None):
404
        encoded_path = path.encode('utf-8')
405
        (mode, tree_sha) = tree_lookup_path(self.store.__getitem__, self.tree,
406
            encoded_path)
407
408
        if not stat.S_ISDIR(mode):
409
            return
410
411
        file_id = self.path2id(path)
412
        tree = self.store[tree_sha]
413
        for name, mode, hexsha in tree.iteritems():
414
            if self.mapping.is_special_file(name):
415
                continue
416
            child_path = posixpath.join(encoded_path, name)
417
            if stat.S_ISDIR(mode):
418
                yield self._get_dir_ie(child_path, file_id)
419
            else:
420
                yield self._get_file_ie(child_path, name, mode, hexsha,
421
                                        file_id)
422
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
423
    def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
0.287.4 by Jelmer Vernooij
Fix GitRevisionTree behaviour for null:
424
        if self.tree is None:
425
            return
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
426
        if yield_parents:
427
            # TODO(jelmer): Support yield parents
428
            raise NotImplementedError
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
429
        if specific_file_ids is not None:
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
430
            specific_paths = [self.id2path(file_id).encode('utf-8') for file_id in specific_file_ids]
431
            if specific_paths in ([""], []):
0.200.1285 by Jelmer Vernooij
Support specific_file_ids argument to Tree.iter_entries_by_dir.
432
                specific_paths = None
433
            else:
434
                specific_paths = set(specific_paths)
435
        else:
436
            specific_paths = None
0.264.10 by Jelmer Vernooij
Yield inventory entries.
437
        todo = set([("", self.tree, None)])
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
438
        while todo:
0.264.10 by Jelmer Vernooij
Yield inventory entries.
439
            path, tree_sha, parent_id = todo.pop()
440
            ie = self._get_dir_ie(path, parent_id)
0.200.1285 by Jelmer Vernooij
Support specific_file_ids argument to Tree.iter_entries_by_dir.
441
            if specific_paths is None or path in specific_paths:
0.200.1715 by Jelmer Vernooij
Fix some more tests.
442
                yield path.decode("utf-8"), ie
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
443
            tree = self.store[tree_sha]
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
444
            for name, mode, hexsha in tree.iteritems():
0.200.1328 by Jelmer Vernooij
More test fixes.
445
                if self.mapping.is_special_file(name):
446
                    continue
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
447
                child_path = posixpath.join(path, name)
448
                if stat.S_ISDIR(mode):
0.200.1285 by Jelmer Vernooij
Support specific_file_ids argument to Tree.iter_entries_by_dir.
449
                    if (specific_paths is None or
450
                        any(filter(lambda p: p.startswith(child_path), specific_paths))):
451
                        todo.add((child_path, hexsha, ie.file_id))
452
                elif specific_paths is None or child_path in specific_paths:
0.200.1715 by Jelmer Vernooij
Fix some more tests.
453
                    yield (child_path.decode("utf-8"),
0.200.1307 by Jelmer Vernooij
Formatting fixes, specify path to a couple more functions.
454
                            self._get_file_ie(child_path, name, mode, hexsha,
0.200.1285 by Jelmer Vernooij
Support specific_file_ids argument to Tree.iter_entries_by_dir.
455
                           ie.file_id))
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
456
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
457
    def get_revision_id(self):
0.200.959 by Jelmer Vernooij
Improve docstrings.
458
        """See RevisionTree.get_revision_id."""
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
459
        return self._revision_id
460
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
461
    def get_file_sha1(self, path, file_id=None, stat_value=None):
0.287.6 by Jelmer Vernooij
Fix some more tests.
462
        if self.tree is None:
463
            raise errors.NoSuchFile(path)
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
464
        return osutils.sha_string(self.get_file_text(path, file_id))
0.200.1255 by Jelmer Vernooij
Implement GitRevisionTree.get_file_sha1.
465
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
466
    def get_file_verifier(self, path, file_id=None, stat_value=None):
0.200.1302 by Jelmer Vernooij
Significantly improve performance of WorkingTree.extras().
467
        (mode, hexsha) = tree_lookup_path(self.store.__getitem__, self.tree,
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
468
            path.encode('utf-8'))
0.200.1302 by Jelmer Vernooij
Significantly improve performance of WorkingTree.extras().
469
        return ("GIT", hexsha)
470
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
471
    def get_file_text(self, path, file_id=None):
0.200.959 by Jelmer Vernooij
Improve docstrings.
472
        """See RevisionTree.get_file_text."""
0.287.5 by Jelmer Vernooij
Fix root handling.
473
        if self.tree is None:
474
            raise errors.NoSuchFile(path)
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
475
        (mode, hexsha) = tree_lookup_path(self.store.__getitem__, self.tree,
476
                path.encode('utf-8'))
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
477
        if stat.S_ISREG(mode):
478
            return self.store[hexsha].data
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
479
        else:
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
480
            return b""
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
481
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
482
    def get_symlink_target(self, path, file_id=None):
0.200.1466 by Jelmer Vernooij
Implement GitRevisionTree.get_symlink_target.
483
        """See RevisionTree.get_symlink_target."""
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
484
        (mode, hexsha) = tree_lookup_path(self.store.__getitem__, self.tree,
485
                path.encode('utf-8'))
0.200.1466 by Jelmer Vernooij
Implement GitRevisionTree.get_symlink_target.
486
        if stat.S_ISLNK(mode):
0.200.1701 by Jelmer Vernooij
Fix a few tests.
487
            return self.store[hexsha].data.decode('utf-8')
0.200.1466 by Jelmer Vernooij
Implement GitRevisionTree.get_symlink_target.
488
        else:
489
            return None
490
0.264.10 by Jelmer Vernooij
Yield inventory entries.
491
    def _comparison_data(self, entry, path):
492
        if entry is None:
493
            return None, False, None
494
        return entry.kind, entry.executable, None
495
0.200.1568 by Jelmer Vernooij
Implement GitRevisionTree.path_content_summary.
496
    def path_content_summary(self, path):
497
        """See Tree.path_content_summary."""
498
        try:
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
499
            (mode, hexsha) = tree_lookup_path(self.store.__getitem__, self.tree, path.encode('utf-8'))
0.200.1568 by Jelmer Vernooij
Implement GitRevisionTree.path_content_summary.
500
        except KeyError:
501
            return ('missing', None, None, None)
502
        kind = mode_kind(mode)
503
        if kind == 'file':
504
            executable = mode_is_executable(mode)
505
            contents = self.store[hexsha].data
506
            return (kind, len(contents), executable, osutils.sha_string(contents))
507
        elif kind == 'symlink':
508
            return (kind, None, None, self.store[hexsha].data)
509
        else:
510
            return (kind, None, None, None)
511
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
512
0.252.43 by Jelmer Vernooij
Some refactoring, support proper file ids in revision deltas.
513
def tree_delta_from_git_changes(changes, mapping,
0.200.1743 by Jelmer Vernooij
Fix some revision delta filtering.
514
        (old_fileid_map, new_fileid_map), specific_files=None,
0.287.5 by Jelmer Vernooij
Fix root handling.
515
        require_versioned=False, include_root=False):
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
516
    """Create a TreeDelta from two git trees.
0.200.959 by Jelmer Vernooij
Improve docstrings.
517
518
    source and target are iterators over tuples with:
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
519
        (filename, sha, mode)
520
    """
521
    ret = delta.TreeDelta()
522
    for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes:
0.287.5 by Jelmer Vernooij
Fix root handling.
523
        if newpath == u'' and not include_root:
524
            continue
0.200.1743 by Jelmer Vernooij
Fix some revision delta filtering.
525
        if not (specific_files is None or
0.287.5 by Jelmer Vernooij
Fix root handling.
526
                (oldpath is not None and osutils.is_inside_or_parent_of_any(specific_files, oldpath)) or
527
                (newpath is not None and osutils.is_inside_or_parent_of_any(specific_files, newpath))):
0.200.1743 by Jelmer Vernooij
Fix some revision delta filtering.
528
            continue
0.200.1752 by Jelmer Vernooij
Don't traverse nested trees in WorkingTree.smart_add.
529
        if mapping.is_special_file(oldpath):
0.252.43 by Jelmer Vernooij
Some refactoring, support proper file ids in revision deltas.
530
            oldpath = None
0.200.1752 by Jelmer Vernooij
Don't traverse nested trees in WorkingTree.smart_add.
531
        if mapping.is_special_file(newpath):
0.252.43 by Jelmer Vernooij
Some refactoring, support proper file ids in revision deltas.
532
            newpath = None
533
        if oldpath is None and newpath is None:
534
            continue
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
535
        if oldpath is None:
0.200.1613 by Jelmer Vernooij
Handle encoding better in working tree iter changes.
536
            file_id = new_fileid_map.lookup_file_id(newpath)
537
            ret.added.append((newpath.decode('utf-8'), file_id, mode_kind(newmode)))
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
538
        elif newpath is None:
0.200.1613 by Jelmer Vernooij
Handle encoding better in working tree iter changes.
539
            file_id = old_fileid_map.lookup_file_id(oldpath)
540
            ret.removed.append((oldpath.decode('utf-8'), file_id, mode_kind(oldmode)))
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
541
        elif oldpath != newpath:
0.200.1613 by Jelmer Vernooij
Handle encoding better in working tree iter changes.
542
            file_id = old_fileid_map.lookup_file_id(oldpath)
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
543
            ret.renamed.append(
544
                (oldpath.decode('utf-8'), newpath.decode('utf-8'), file_id,
545
                mode_kind(newmode), (oldsha != newsha),
546
                (oldmode != newmode)))
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
547
        elif mode_kind(oldmode) != mode_kind(newmode):
0.200.1613 by Jelmer Vernooij
Handle encoding better in working tree iter changes.
548
            file_id = new_fileid_map.lookup_file_id(newpath)
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
549
            ret.kind_changed.append(
550
                (newpath.decode('utf-8'), file_id, mode_kind(oldmode),
551
                mode_kind(newmode)))
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
552
        elif oldsha != newsha or oldmode != newmode:
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
553
            if stat.S_ISDIR(oldmode) and stat.S_ISDIR(newmode):
554
                continue
0.200.1613 by Jelmer Vernooij
Handle encoding better in working tree iter changes.
555
            file_id = new_fileid_map.lookup_file_id(newpath)
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
556
            ret.modified.append(
557
                (newpath.decode('utf-8'), file_id, mode_kind(newmode),
558
                (oldsha != newsha), (oldmode != newmode)))
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
559
        else:
0.200.1613 by Jelmer Vernooij
Handle encoding better in working tree iter changes.
560
            file_id = new_fileid_map.lookup_file_id(newpath)
561
            ret.unchanged.append((newpath.decode('utf-8'), file_id, mode_kind(newmode)))
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
562
    return ret
563
564
0.357.3 by Jelmer Vernooij
More remove fixes.
565
def changes_from_git_changes(changes, mapping, specific_files=None, include_unchanged=False):
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
566
    """Create a iter_changes-like generator from a git stream.
0.200.959 by Jelmer Vernooij
Improve docstrings.
567
568
    source and target are iterators over tuples with:
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
569
        (filename, sha, mode)
570
    """
571
    for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes:
0.200.1743 by Jelmer Vernooij
Fix some revision delta filtering.
572
        if not (specific_files is None or
0.287.5 by Jelmer Vernooij
Fix root handling.
573
                (oldpath is not None and osutils.is_inside_or_parent_of_any(specific_files, oldpath)) or
574
                (newpath is not None and osutils.is_inside_or_parent_of_any(specific_files, newpath))):
0.200.1743 by Jelmer Vernooij
Fix some revision delta filtering.
575
            continue
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
576
        path = (oldpath, newpath)
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
577
        if oldpath is not None and mapping.is_special_file(oldpath):
578
            continue
579
        if newpath is not None and mapping.is_special_file(newpath):
0.200.1328 by Jelmer Vernooij
More test fixes.
580
            continue
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
581
        if oldpath is None:
582
            fileid = mapping.generate_file_id(newpath)
583
            oldexe = None
584
            oldkind = None
585
            oldname = None
586
            oldparent = None
587
        else:
0.200.1345 by Jelmer Vernooij
paths should be unicode when yielded by iter_changes.
588
            oldpath = oldpath.decode("utf-8")
0.361.1 by Jelmer Vernooij
Don't use assert.
589
            if oldmode is None:
590
                raise ValueError
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
591
            oldexe = mode_is_executable(oldmode)
592
            oldkind = mode_kind(oldmode)
0.316.1 by Jelmer Vernooij
Fix iter_changes behaviour for trees in the tree root.
593
            if oldpath == u'':
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
594
                oldparent = None
0.316.1 by Jelmer Vernooij
Fix iter_changes behaviour for trees in the tree root.
595
                oldname = ''
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
596
            else:
0.316.1 by Jelmer Vernooij
Fix iter_changes behaviour for trees in the tree root.
597
                (oldparentpath, oldname) = osutils.split(oldpath)
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
598
                oldparent = mapping.generate_file_id(oldparentpath)
599
            fileid = mapping.generate_file_id(oldpath)
600
        if newpath is None:
601
            newexe = None
602
            newkind = None
603
            newname = None
604
            newparent = None
605
        else:
0.200.1345 by Jelmer Vernooij
paths should be unicode when yielded by iter_changes.
606
            newpath = newpath.decode("utf-8")
0.200.1576 by Jelmer Vernooij
Merge a bunch of fixes from store-roundtrip-info.
607
            if newmode is not None:
608
                newexe = mode_is_executable(newmode)
609
                newkind = mode_kind(newmode)
610
            else:
611
                newexe = False
612
                newkind = None
0.316.1 by Jelmer Vernooij
Fix iter_changes behaviour for trees in the tree root.
613
            if newpath == u'':
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
614
                newparent = None
0.316.1 by Jelmer Vernooij
Fix iter_changes behaviour for trees in the tree root.
615
                newname = u''
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
616
            else:
0.316.1 by Jelmer Vernooij
Fix iter_changes behaviour for trees in the tree root.
617
                newparentpath, newname = osutils.split(newpath)
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
618
                newparent = mapping.generate_file_id(newparentpath)
0.357.3 by Jelmer Vernooij
More remove fixes.
619
        if (not include_unchanged and
620
            oldkind == 'directory' and newkind == 'directory' and
621
            oldpath == newpath):
622
            continue
0.200.959 by Jelmer Vernooij
Improve docstrings.
623
        yield (fileid, (oldpath, newpath), (oldsha != newsha),
624
             (oldpath is not None, newpath is not None),
625
             (oldparent, newparent), (oldname, newname),
626
             (oldkind, newkind), (oldexe, newexe))
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
627
628
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
629
class InterGitTrees(_mod_tree.InterTree):
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
630
    """InterTree that works between two git trees."""
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
631
0.200.659 by Jelmer Vernooij
Prevent tests using InterGitRevisionTrees.
632
    _matching_from_tree_format = None
633
    _matching_to_tree_format = None
634
    _test_mutable_trees_to_test_trees = None
635
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
636
    @classmethod
637
    def is_compatible(cls, source, target):
0.200.959 by Jelmer Vernooij
Improve docstrings.
638
        return (isinstance(source, GitRevisionTree) and
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
639
                isinstance(target, GitRevisionTree))
640
641
    def compare(self, want_unchanged=False, specific_files=None,
642
                extra_trees=None, require_versioned=False, include_root=False,
643
                want_unversioned=False):
0.357.1 by Jelmer Vernooij
Fix some remove tests.
644
        changes = self._iter_git_changes(want_unchanged=want_unchanged,
645
                require_versioned=require_versioned,
646
                specific_files=specific_files)
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
647
        source_fileid_map = self.source._fileid_map
648
        target_fileid_map = self.target._fileid_map
0.200.959 by Jelmer Vernooij
Improve docstrings.
649
        return tree_delta_from_git_changes(changes, self.target.mapping,
0.252.43 by Jelmer Vernooij
Some refactoring, support proper file ids in revision deltas.
650
            (source_fileid_map, target_fileid_map),
0.287.5 by Jelmer Vernooij
Fix root handling.
651
            specific_files=specific_files, include_root=include_root)
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
652
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
653
    def iter_changes(self, include_unchanged=False, specific_files=None,
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
654
                     pb=None, extra_trees=[], require_versioned=True,
655
                     want_unversioned=False):
0.357.1 by Jelmer Vernooij
Fix some remove tests.
656
        changes = self._iter_git_changes(want_unchanged=include_unchanged,
657
                require_versioned=require_versioned,
658
                specific_files=specific_files)
0.200.959 by Jelmer Vernooij
Improve docstrings.
659
        return changes_from_git_changes(changes, self.target.mapping,
0.357.3 by Jelmer Vernooij
More remove fixes.
660
            specific_files=specific_files, include_unchanged=include_unchanged)
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
661
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
662
    def _iter_git_changes(self, want_unchanged=False):
663
        raise NotImplementedError(self._iter_git_changes)
664
665
666
class InterGitRevisionTrees(InterGitTrees):
667
    """InterTree that works between two git revision trees."""
668
669
    _matching_from_tree_format = None
670
    _matching_to_tree_format = None
671
    _test_mutable_trees_to_test_trees = None
672
673
    @classmethod
674
    def is_compatible(cls, source, target):
675
        return (isinstance(source, GitRevisionTree) and
676
                isinstance(target, GitRevisionTree))
677
0.357.1 by Jelmer Vernooij
Fix some remove tests.
678
    def _iter_git_changes(self, want_unchanged=False, require_versioned=False,
679
            specific_files=None):
680
        if require_versioned and specific_files:
681
            for path in specific_files:
682
                if (not self.source.is_versioned(path) and
683
                    not self.target.is_versioned(path)):
684
                    raise errors.PathsNotVersionedError(path)
685
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
686
        if self.source._repository._git.object_store != self.target._repository._git.object_store:
0.200.1780 by Jelmer Vernooij
Fix cross-object-store tree comparison.
687
            store = OverlayObjectStore([self.source._repository._git.object_store,
688
                                        self.target._repository._git.object_store])
689
        else:
690
            store = self.source._repository._git.object_store
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
691
        return self.source._repository._git.object_store.tree_changes(
692
            self.source.tree, self.target.tree, want_unchanged=want_unchanged,
0.339.1 by Jelmer Vernooij
Use change_type_same.
693
            include_trees=True, change_type_same=True)
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
694
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
695
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
696
_mod_tree.InterTree.register_optimiser(InterGitRevisionTrees)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
697
698
699
class MutableGitIndexTree(mutabletree.MutableTree):
700
701
    def __init__(self):
702
        self._lock_mode = None
703
        self._lock_count = 0
704
        self._versioned_dirs = None
705
706
    def is_versioned(self, path):
707
        with self.lock_read():
708
            path = path.rstrip('/').encode('utf-8')
709
            return (path in self.index or self._has_dir(path))
710
711
    def _has_dir(self, path):
712
        if path == "":
713
            return True
714
        if self._versioned_dirs is None:
715
            self._load_dirs()
716
        return path in self._versioned_dirs
717
718
    def _load_dirs(self):
0.361.3 by Jelmer Vernooij
Merge trunk,
719
        if self._lock_mode is None:
720
            raise errors.ObjectNotLocked(self)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
721
        self._versioned_dirs = set()
722
        for p in self.index:
723
            self._ensure_versioned_dir(posixpath.dirname(p))
724
725
    def _ensure_versioned_dir(self, dirname):
726
        if dirname in self._versioned_dirs:
727
            return
728
        if dirname != "":
729
            self._ensure_versioned_dir(posixpath.dirname(dirname))
730
        self._versioned_dirs.add(dirname)
731
732
    def path2id(self, path):
733
        with self.lock_read():
734
            path = path.rstrip('/')
735
            if self.is_versioned(path.rstrip('/')):
736
                return self._fileid_map.lookup_file_id(path.encode("utf-8"))
737
            return None
738
739
    def has_id(self, file_id):
740
        try:
741
            self.id2path(file_id)
742
        except errors.NoSuchId:
743
            return False
744
        else:
745
            return True
746
747
    def id2path(self, file_id):
748
        if file_id is None:
749
            return ''
0.361.3 by Jelmer Vernooij
Merge trunk,
750
        if type(file_id) is not bytes:
751
            raise TypeError(file_id)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
752
        with self.lock_read():
753
            try:
754
                path = self._fileid_map.lookup_path(file_id)
755
            except ValueError:
756
                raise errors.NoSuchId(self, file_id)
757
            path = path.decode('utf-8')
758
            if self.is_versioned(path):
759
                return path
760
            raise errors.NoSuchId(self, file_id)
761
762
    def _set_root_id(self, file_id):
763
        self._fileid_map.set_file_id("", file_id)
764
765
    def get_root_id(self):
766
        return self.path2id("")
767
768
    def _add(self, files, ids, kinds):
769
        for (path, file_id, kind) in zip(files, ids, kinds):
770
            if file_id is not None:
771
                raise workingtree.SettingFileIdUnsupported()
0.366.1 by Jelmer Vernooij
Fix normalized filename checking in add.
772
            path, can_access = osutils.normalized_filename(path)
773
            if not can_access:
774
                raise errors.InvalidNormalization(path)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
775
            self._index_add_entry(path, kind)
776
777
    def _index_add_entry(self, path, kind, flags=0):
0.361.4 by Jelmer Vernooij
Fix assert.
778
        if not isinstance(path, basestring):
0.361.3 by Jelmer Vernooij
Merge trunk,
779
            raise TypeError(path)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
780
        if kind == "directory":
781
            # Git indexes don't contain directories
782
            return
783
        if kind == "file":
784
            blob = Blob()
785
            try:
786
                file, stat_val = self.get_file_with_stat(path)
787
            except (errors.NoSuchFile, IOError):
788
                # TODO: Rather than come up with something here, use the old index
789
                file = BytesIO()
790
                stat_val = os.stat_result(
791
                    (stat.S_IFREG | 0644, 0, 0, 0, 0, 0, 0, 0, 0, 0))
792
            blob.set_raw_string(file.read())
793
        elif kind == "symlink":
794
            blob = Blob()
795
            try:
796
                stat_val = self._lstat(path)
797
            except (errors.NoSuchFile, OSError):
798
                # TODO: Rather than come up with something here, use the
799
                # old index
800
                stat_val = os.stat_result(
801
                    (stat.S_IFLNK, 0, 0, 0, 0, 0, 0, 0, 0, 0))
802
            blob.set_raw_string(
803
                self.get_symlink_target(path).encode("utf-8"))
804
        else:
805
            raise AssertionError("unknown kind '%s'" % kind)
806
        # Add object to the repository if it didn't exist yet
807
        if not blob.id in self.store:
808
            self.store.add_object(blob)
809
        # Add an entry to the index or update the existing entry
810
        ensure_normalized_path(path)
811
        encoded_path = path.encode("utf-8")
812
        if b'\r' in encoded_path or b'\n' in encoded_path:
813
            # TODO(jelmer): Why do we need to do this?
814
            trace.mutter('ignoring path with invalid newline in it: %r', path)
815
            return
816
        self.index[encoded_path] = index_entry_from_stat(
817
            stat_val, blob.id, flags)
818
        if self._versioned_dirs is not None:
819
            self._ensure_versioned_dir(encoded_path)
820
821
    def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
822
        if yield_parents:
823
            raise NotImplementedError(self.iter_entries_by_dir)
824
        with self.lock_read():
825
            if specific_file_ids is not None:
826
                specific_paths = []
827
                for file_id in specific_file_ids:
828
                    if file_id is None:
829
                        continue
830
                    try:
831
                        specific_paths.append(self.id2path(file_id))
832
                    except errors.NoSuchId:
833
                        pass
0.372.1 by Jelmer Vernooij
Fix some missing id handling.
834
                specific_paths = set(specific_paths)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
835
            else:
836
                specific_paths = None
837
            root_ie = self._get_dir_ie(u"", None)
838
            ret = {}
0.372.1 by Jelmer Vernooij
Fix some missing id handling.
839
            if specific_paths is None or u"" in specific_paths:
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
840
                ret[(None, u"")] = root_ie
841
            dir_ids = {u"": root_ie.file_id}
842
            for path, value in self.index.iteritems():
843
                if self.mapping.is_special_file(path):
844
                    continue
845
                path = path.decode("utf-8")
846
                if specific_paths is not None and not path in specific_paths:
847
                    continue
848
                (parent, name) = posixpath.split(path)
849
                try:
850
                    file_ie = self._get_file_ie(name, path, value, None)
851
                except errors.NoSuchFile:
852
                    continue
853
                if yield_parents or specific_file_ids is None:
854
                    for (dir_path, dir_ie) in self._add_missing_parent_ids(parent,
855
                            dir_ids):
856
                        ret[(posixpath.dirname(dir_path), dir_path)] = dir_ie
857
                file_ie.parent_id = self.path2id(parent)
858
                ret[(posixpath.dirname(path), path)] = file_ie
859
            return ((path, ie) for ((_, path), ie) in sorted(ret.items()))
860
861
862
    def _get_dir_ie(self, path, parent_id):
863
        file_id = self.path2id(path)
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
864
        return GitTreeDirectory(file_id,
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
865
            posixpath.basename(path).strip("/"), parent_id)
866
867
    def _get_file_ie(self, name, path, value, parent_id):
0.361.3 by Jelmer Vernooij
Merge trunk,
868
        if type(name) is not unicode:
869
            raise TypeError(name)
870
        if type(path) is not unicode:
871
            raise TypeError(path)
872
        if not isinstance(value, tuple) or len(value) != 10:
873
            raise TypeError(value)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
874
        (ctime, mtime, dev, ino, mode, uid, gid, size, sha, flags) = value
875
        file_id = self.path2id(path)
876
        if type(file_id) != str:
877
            raise AssertionError
878
        kind = mode_kind(mode)
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
879
        ie = entry_factory[kind](file_id, name, parent_id)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
880
        if kind == 'symlink':
881
            ie.symlink_target = self.get_symlink_target(path, file_id)
882
        else:
883
            try:
884
                data = self.get_file_text(path, file_id)
885
            except errors.NoSuchFile:
886
                data = None
887
            except IOError as e:
888
                if e.errno != errno.ENOENT:
889
                    raise
890
                data = None
891
            if data is None:
892
                data = self.branch.repository._git.object_store[sha].data
893
            ie.text_sha1 = osutils.sha_string(data)
894
            ie.text_size = len(data)
895
            ie.executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
896
        ie.revision = None
897
        return ie
898
899
    def _add_missing_parent_ids(self, path, dir_ids):
900
        if path in dir_ids:
901
            return []
902
        parent = posixpath.dirname(path).strip("/")
903
        ret = self._add_missing_parent_ids(parent, dir_ids)
904
        parent_id = dir_ids[parent]
905
        ie = self._get_dir_ie(path, parent_id)
906
        dir_ids[path] = ie.file_id
907
        ret.append((path, ie))
908
        return ret
909
910
    def _comparison_data(self, entry, path):
911
        if entry is None:
912
            return None, False, None
913
        return entry.kind, entry.executable, None
914
915
    def _unversion_path(self, path):
0.361.3 by Jelmer Vernooij
Merge trunk,
916
        if self._lock_mode is None:
917
            raise errors.ObjectNotLocked(self)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
918
        encoded_path = path.encode("utf-8")
919
        count = 0
920
        try:
921
            del self.index[encoded_path]
922
        except KeyError:
923
            # A directory, perhaps?
924
            for p in list(self.index):
925
                if p.startswith(encoded_path+b"/"):
926
                    count += 1
927
                    del self.index[p]
928
        else:
929
            count = 1
930
        self._versioned_dirs = None
931
        return count
932
933
    def unversion(self, paths, file_ids=None):
934
        with self.lock_tree_write():
935
            for path in paths:
936
                if self._unversion_path(path) == 0:
937
                    raise errors.NoSuchFile(path)
938
            self._versioned_dirs = None
939
            self.flush()
940
941
    def flush(self):
942
        pass
943
944
    def update_basis_by_delta(self, revid, delta):
945
        # TODO(jelmer): This shouldn't be called, it's inventory specific.
946
        for (old_path, new_path, file_id, ie) in delta:
947
            if old_path is not None and old_path.encode('utf-8') in self.index:
948
                del self.index[old_path.encode('utf-8')]
949
                self._versioned_dirs = None
950
            if new_path is not None and ie.kind != 'directory':
951
                self._index_add_entry(new_path, ie.kind)
952
        self.flush()
953
        self._set_merges_from_parent_ids([])
0.360.4 by Jelmer Vernooij
Implement MemoryTree.rename_one, MemoryTree.mkdir.
954
955
    def move(self, from_paths, to_dir=None, after=None):
956
        rename_tuples = []
957
        with self.lock_tree_write():
958
            to_abs = self.abspath(to_dir)
959
            if not os.path.isdir(to_abs):
960
                raise errors.BzrMoveFailedError('', to_dir,
961
                    errors.NotADirectory(to_abs))
962
963
            for from_rel in from_paths:
964
                from_tail = os.path.split(from_rel)[-1]
965
                to_rel = os.path.join(to_dir, from_tail)
966
                self.rename_one(from_rel, to_rel, after=after)
967
                rename_tuples.append((from_rel, to_rel))
968
            self.flush()
969
            return rename_tuples
970
971
    def rename_one(self, from_rel, to_rel, after=None):
972
        from_path = from_rel.encode("utf-8")
973
        to_rel, can_access = osutils.normalized_filename(to_rel)
974
        if not can_access:
975
            raise errors.InvalidNormalization(to_rel)
976
        to_path = to_rel.encode("utf-8")
977
        with self.lock_tree_write():
978
            if not after:
979
                # Perhaps it's already moved?
980
                after = (
981
                    not self.has_filename(from_rel) and
982
                    self.has_filename(to_rel) and
983
                    not self.is_versioned(to_rel))
984
            if after:
985
                if not self.has_filename(to_rel):
986
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
987
                        errors.NoSuchFile(to_rel))
988
                if self.basis_tree().is_versioned(to_rel):
989
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
990
                        errors.AlreadyVersionedError(to_rel))
991
992
                kind = self.kind(to_rel)
993
            else:
994
                try:
995
                    to_kind = self.kind(to_rel)
996
                except errors.NoSuchFile:
997
                    exc_type = errors.BzrRenameFailedError
998
                    to_kind = None
999
                else:
1000
                    exc_type = errors.BzrMoveFailedError
1001
                if self.is_versioned(to_rel):
1002
                    raise exc_type(from_rel, to_rel,
1003
                        errors.AlreadyVersionedError(to_rel))
1004
                if not self.has_filename(from_rel):
1005
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
1006
                        errors.NoSuchFile(from_rel))
1007
                if not self.is_versioned(from_rel):
1008
                    raise exc_type(from_rel, to_rel,
1009
                        errors.NotVersionedError(from_rel))
1010
                if self.has_filename(to_rel):
1011
                    raise errors.RenameFailedFilesExist(
1012
                        from_rel, to_rel, errors.FileExists(to_rel))
1013
1014
                kind = self.kind(from_rel)
1015
1016
            if not after and not from_path in self.index and kind != 'directory':
1017
                # It's not a file
1018
                raise errors.BzrMoveFailedError(from_rel, to_rel,
1019
                    errors.NotVersionedError(path=from_rel))
1020
1021
            if not after:
1022
                try:
1023
                    self._rename_one(from_rel, to_rel)
1024
                except OSError as e:
1025
                    if e.errno == errno.ENOENT:
1026
                        raise errors.BzrMoveFailedError(from_rel, to_rel,
1027
                            errors.NoSuchFile(to_rel))
1028
                    raise
1029
            if kind != 'directory':
1030
                try:
1031
                    del self.index[from_path]
1032
                except KeyError:
1033
                    pass
1034
                self._index_add_entry(to_rel, kind)
1035
            else:
1036
                todo = [p for p in self.index if p.startswith(from_path+'/')]
1037
                for p in todo:
1038
                    self.index[posixpath.join(to_path, posixpath.relpath(p, from_path))] = self.index[p]
1039
                    del self.index[p]
1040
1041
            self._versioned_dirs = None
1042
            self.flush()