/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 (
6973.1.1 by Jelmer Vernooij
Make InterIndexGitTree suitable for use with MemoryGitTree.
27
    blob_from_path_and_stat,
28
    cleanup_mode,
29
    commit_tree,
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
30
    index_entry_from_stat,
31
    )
0.200.1780 by Jelmer Vernooij
Fix cross-object-store tree comparison.
32
from dulwich.object_store import (
33
    tree_lookup_path,
34
    OverlayObjectStore,
35
    )
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
36
from dulwich.objects import (
37
    Blob,
38
    Tree,
0.372.1 by Jelmer Vernooij
Fix some missing id handling.
39
    ZERO_SHA,
0.429.2 by Jelmer Vernooij
Some more work on submodule support.
40
    S_IFGITLINK,
0.429.7 by Jelmer Vernooij
Consistent file ids.
41
    S_ISGITLINK,
0.429.2 by Jelmer Vernooij
Some more work on submodule support.
42
    )
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
43
import stat
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
44
import posixpath
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
45
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
46
from ... import (
0.429.26 by Jelmer Vernooij
Fix remaining test.
47
    controldir as _mod_controldir,
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
48
    delta,
49
    errors,
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
50
    lock,
51
    mutabletree,
0.264.10 by Jelmer Vernooij
Yield inventory entries.
52
    osutils,
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
53
    revisiontree,
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
54
    trace,
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
55
    tree as _mod_tree,
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
56
    workingtree,
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
57
    )
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
58
from ...revision import NULL_REVISION
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
59
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
60
from .mapping import (
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
61
    mode_is_executable,
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
62
    mode_kind,
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
63
    GitFileIdMap,
0.287.2 by Jelmer Vernooij
Support empty trees in GitRevisionTree.
64
    default_mapping,
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
65
    )
66
67
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
68
class GitTreeDirectory(_mod_tree.TreeDirectory):
69
0.390.1 by Jelmer Vernooij
Don't set .revision for returned TreeEntry objects; it's slow, and callers can call Tree.get_file_revision if they really care.
70
    __slots__ = ['file_id', 'name', 'parent_id', 'children']
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
71
0.390.1 by Jelmer Vernooij
Don't set .revision for returned TreeEntry objects; it's slow, and callers can call Tree.get_file_revision if they really care.
72
    def __init__(self, file_id, name, parent_id):
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
73
        self.file_id = file_id
74
        self.name = name
75
        self.parent_id = parent_id
76
        # TODO(jelmer)
77
        self.children = {}
78
79
    @property
80
    def kind(self):
81
        return 'directory'
82
83
    @property
84
    def executable(self):
85
        return False
86
87
    def copy(self):
88
        return self.__class__(
0.390.1 by Jelmer Vernooij
Don't set .revision for returned TreeEntry objects; it's slow, and callers can call Tree.get_file_revision if they really care.
89
            self.file_id, self.name, self.parent_id)
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
90
91
    def __repr__(self):
0.390.1 by Jelmer Vernooij
Don't set .revision for returned TreeEntry objects; it's slow, and callers can call Tree.get_file_revision if they really care.
92
        return "%s(file_id=%r, name=%r, parent_id=%r)" % (
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
93
            self.__class__.__name__, self.file_id, self.name,
0.390.1 by Jelmer Vernooij
Don't set .revision for returned TreeEntry objects; it's slow, and callers can call Tree.get_file_revision if they really care.
94
            self.parent_id)
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
95
96
    def __eq__(self, other):
97
        return (self.kind == other.kind and
98
                self.file_id == other.file_id and
99
                self.name == other.name and
0.390.1 by Jelmer Vernooij
Don't set .revision for returned TreeEntry objects; it's slow, and callers can call Tree.get_file_revision if they really care.
100
                self.parent_id == other.parent_id)
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
101
102
103
class GitTreeFile(_mod_tree.TreeFile):
104
0.390.1 by Jelmer Vernooij
Don't set .revision for returned TreeEntry objects; it's slow, and callers can call Tree.get_file_revision if they really care.
105
    __slots__ = ['file_id', 'name', 'parent_id', 'text_size', 'text_sha1',
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
106
                 'executable']
107
0.390.2 by Jelmer Vernooij
merge trunk
108
    def __init__(self, file_id, name, parent_id, text_size=None,
0.389.2 by Jelmer Vernooij
Add repr implementation to GitTreeFile & GitTreeLink.
109
                 text_sha1=None, executable=None):
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
110
        self.file_id = file_id
111
        self.name = name
112
        self.parent_id = parent_id
0.389.2 by Jelmer Vernooij
Add repr implementation to GitTreeFile & GitTreeLink.
113
        self.text_size = text_size
114
        self.text_sha1 = text_sha1
115
        self.executable = executable
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
116
117
    @property
118
    def kind(self):
119
        return 'file'
120
121
    def __eq__(self, other):
122
        return (self.kind == other.kind and
123
                self.file_id == other.file_id and
124
                self.name == other.name and
125
                self.parent_id == other.parent_id and
126
                self.text_sha1 == other.text_sha1 and
127
                self.text_size == other.text_size and
128
                self.executable == other.executable)
129
0.389.2 by Jelmer Vernooij
Add repr implementation to GitTreeFile & GitTreeLink.
130
    def __repr__(self):
0.390.2 by Jelmer Vernooij
merge trunk
131
        return "%s(file_id=%r, name=%r, parent_id=%r, text_size=%r, text_sha1=%r, executable=%r)" % (
0.389.2 by Jelmer Vernooij
Add repr implementation to GitTreeFile & GitTreeLink.
132
            type(self).__name__, self.file_id, self.name, self.parent_id,
0.390.2 by Jelmer Vernooij
merge trunk
133
            self.text_size, self.text_sha1, self.executable)
0.389.2 by Jelmer Vernooij
Add repr implementation to GitTreeFile & GitTreeLink.
134
0.365.2 by Jelmer Vernooij
Add eq/copy.
135
    def copy(self):
136
        ret = self.__class__(
0.390.1 by Jelmer Vernooij
Don't set .revision for returned TreeEntry objects; it's slow, and callers can call Tree.get_file_revision if they really care.
137
                self.file_id, self.name, self.parent_id)
0.365.2 by Jelmer Vernooij
Add eq/copy.
138
        ret.text_sha1 = self.text_sha1
139
        ret.text_size = self.text_size
140
        ret.executable = self.executable
141
        return ret
142
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
143
144
class GitTreeSymlink(_mod_tree.TreeLink):
145
0.390.1 by Jelmer Vernooij
Don't set .revision for returned TreeEntry objects; it's slow, and callers can call Tree.get_file_revision if they really care.
146
    __slots__ = ['file_id', 'name', 'parent_id', 'symlink_target']
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
147
0.390.1 by Jelmer Vernooij
Don't set .revision for returned TreeEntry objects; it's slow, and callers can call Tree.get_file_revision if they really care.
148
    def __init__(self, file_id, name, parent_id,
0.365.2 by Jelmer Vernooij
Add eq/copy.
149
                 symlink_target=None):
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
150
        self.file_id = file_id
151
        self.name = name
152
        self.parent_id = parent_id
0.365.2 by Jelmer Vernooij
Add eq/copy.
153
        self.symlink_target = symlink_target
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
154
155
    @property
156
    def kind(self):
157
        return 'symlink'
158
159
    @property
160
    def executable(self):
161
        return False
162
163
    @property
164
    def text_size(self):
165
        return None
166
0.389.2 by Jelmer Vernooij
Add repr implementation to GitTreeFile & GitTreeLink.
167
    def __repr__(self):
0.390.2 by Jelmer Vernooij
merge trunk
168
        return "%s(file_id=%r, name=%r, parent_id=%r, symlink_target=%r)" % (
0.389.2 by Jelmer Vernooij
Add repr implementation to GitTreeFile & GitTreeLink.
169
            type(self).__name__, self.file_id, self.name, self.parent_id,
0.390.2 by Jelmer Vernooij
merge trunk
170
            self.symlink_target)
0.389.2 by Jelmer Vernooij
Add repr implementation to GitTreeFile & GitTreeLink.
171
0.365.2 by Jelmer Vernooij
Add eq/copy.
172
    def __eq__(self, other):
173
        return (self.kind == other.kind and
174
                self.file_id == other.file_id and
175
                self.name == other.name and
176
                self.parent_id == other.parent_id and
177
                self.symlink_target == other.symlink_target)
178
179
    def copy(self):
180
        return self.__class__(
181
                self.file_id, self.name, self.parent_id,
0.390.1 by Jelmer Vernooij
Don't set .revision for returned TreeEntry objects; it's slow, and callers can call Tree.get_file_revision if they really care.
182
                self.symlink_target)
0.365.2 by Jelmer Vernooij
Add eq/copy.
183
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
184
0.429.5 by Jelmer Vernooij
Fix tree_content_summary test.
185
class GitTreeSubmodule(_mod_tree.TreeLink):
186
0.429.12 by Jelmer Vernooij
Remove revision.
187
    __slots__ = ['file_id', 'name', 'parent_id', 'reference_revision']
0.429.5 by Jelmer Vernooij
Fix tree_content_summary test.
188
0.429.12 by Jelmer Vernooij
Remove revision.
189
    def __init__(self, file_id, name, parent_id, reference_revision=None):
0.429.5 by Jelmer Vernooij
Fix tree_content_summary test.
190
        self.file_id = file_id
191
        self.name = name
192
        self.parent_id = parent_id
193
        self.reference_revision = reference_revision
194
195
    @property
196
    def kind(self):
197
        return 'tree-reference'
198
199
    def __repr__(self):
0.429.12 by Jelmer Vernooij
Remove revision.
200
        return "%s(file_id=%r, name=%r, parent_id=%r, reference_revision=%r)" % (
0.429.5 by Jelmer Vernooij
Fix tree_content_summary test.
201
            type(self).__name__, self.file_id, self.name, self.parent_id,
0.429.12 by Jelmer Vernooij
Remove revision.
202
            self.reference_revision)
0.429.5 by Jelmer Vernooij
Fix tree_content_summary test.
203
204
    def __eq__(self, other):
205
        return (self.kind == other.kind and
206
                self.file_id == other.file_id and
207
                self.name == other.name and
208
                self.parent_id == other.parent_id and
209
                self.reference_revision == other.reference_revision)
210
211
    def copy(self):
212
        return self.__class__(
213
                self.file_id, self.name, self.parent_id,
0.429.12 by Jelmer Vernooij
Remove revision.
214
                self.reference_revision)
0.429.5 by Jelmer Vernooij
Fix tree_content_summary test.
215
216
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
217
entry_factory = {
218
    'directory': GitTreeDirectory,
219
    'file': GitTreeFile,
220
    'symlink': GitTreeSymlink,
0.429.5 by Jelmer Vernooij
Fix tree_content_summary test.
221
    'tree-reference': GitTreeSubmodule,
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
222
    }
223
224
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
225
def ensure_normalized_path(path):
226
    """Check whether path is normalized.
227
228
    :raises InvalidNormalization: When path is not normalized, and cannot be
229
        accessed on this platform by the normalized path.
230
    :return: The NFC normalised version of path.
231
    """
232
    norm_path, can_access = osutils.normalized_filename(path)
233
    if norm_path != path:
234
        if can_access:
235
            return norm_path
236
        else:
237
            raise errors.InvalidNormalization(path)
238
    return path
239
240
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
241
class GitRevisionTree(revisiontree.RevisionTree):
0.200.959 by Jelmer Vernooij
Improve docstrings.
242
    """Revision tree implementation based on Git objects."""
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
243
244
    def __init__(self, repository, revision_id):
245
        self._revision_id = revision_id
246
        self._repository = repository
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
247
        self.store = repository._git.object_store
6964.2.3 by Jelmer Vernooij
Review comments.
248
        if not isinstance(revision_id, bytes):
0.361.1 by Jelmer Vernooij
Don't use assert.
249
            raise TypeError(revision_id)
0.200.1283 by Jelmer Vernooij
Provide Repository.get_file_graph() and Tree.get_file_revision().
250
        self.commit_id, self.mapping = repository.lookup_bzr_revision_id(revision_id)
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
251
        if revision_id == NULL_REVISION:
252
            self.tree = None
0.287.2 by Jelmer Vernooij
Support empty trees in GitRevisionTree.
253
            self.mapping = default_mapping
254
            self._fileid_map = GitFileIdMap(
0.287.4 by Jelmer Vernooij
Fix GitRevisionTree behaviour for null:
255
                {},
0.287.2 by Jelmer Vernooij
Support empty trees in GitRevisionTree.
256
                default_mapping)
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
257
        else:
258
            try:
259
                commit = self.store[self.commit_id]
6964.2.1 by Jelmer Vernooij
Initial work to support brz-git on python3.
260
            except KeyError:
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
261
                raise errors.NoSuchRevision(repository, revision_id)
262
            self.tree = commit.tree
263
            self._fileid_map = self.mapping.get_fileid_map(self.store.__getitem__, self.tree)
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
264
0.429.26 by Jelmer Vernooij
Fix remaining test.
265
    def _get_nested_repository(self, path):
266
        nested_repo_transport = self._repository.user_transport.clone(path)
267
        nested_controldir = _mod_controldir.ControlDir.open_from_transport(nested_repo_transport)
268
        return nested_controldir.find_repository()
269
0.349.1 by Jelmer Vernooij
Support supports_rename_tracking method.
270
    def supports_rename_tracking(self):
271
        return False
272
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
273
    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().
274
        change_scanner = self._repository._file_change_scanner
0.372.1 by Jelmer Vernooij
Fix some missing id handling.
275
        if self.commit_id == ZERO_SHA:
276
            return NULL_REVISION
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
277
        (path, commit_id) = change_scanner.find_last_change_revision(
278
            path.encode('utf-8'), self.commit_id)
0.200.1283 by Jelmer Vernooij
Provide Repository.get_file_graph() and Tree.get_file_revision().
279
        return self._repository.lookup_foreign_revision_id(commit_id, self.mapping)
280
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
281
    def get_file_mtime(self, path, file_id=None):
6965.1.1 by Jelmer Vernooij
Add basic support for horizoned history.
282
        try:
283
            revid = self.get_file_revision(path, file_id)
284
        except KeyError:
285
            raise _mod_tree.FileTimestampUnavailable(path)
0.200.1283 by Jelmer Vernooij
Provide Repository.get_file_graph() and Tree.get_file_revision().
286
        try:
287
            rev = self._repository.get_revision(revid)
288
        except errors.NoSuchRevision:
6965.1.1 by Jelmer Vernooij
Add basic support for horizoned history.
289
            raise _mod_tree.FileTimestampUnavailable(path)
0.200.1283 by Jelmer Vernooij
Provide Repository.get_file_graph() and Tree.get_file_revision().
290
        return rev.timestamp
291
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
292
    def id2path(self, file_id):
0.200.1712 by Jelmer Vernooij
Add file_id prefix.
293
        try:
0.329.1 by Jelmer Vernooij
Check that path actually exists in Tree.id2path.
294
            path = self._fileid_map.lookup_path(file_id)
0.200.1712 by Jelmer Vernooij
Add file_id prefix.
295
        except ValueError:
0.200.1714 by Jelmer Vernooij
Fix NoSuchId raising.
296
            raise errors.NoSuchId(self, file_id)
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
297
        path = path.decode('utf-8')
0.372.1 by Jelmer Vernooij
Fix some missing id handling.
298
        if self.is_versioned(path):
0.329.1 by Jelmer Vernooij
Check that path actually exists in Tree.id2path.
299
            return path
300
        raise errors.NoSuchId(self, file_id)
301
302
    def is_versioned(self, path):
303
        return self.has_filename(path)
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
304
305
    def path2id(self, path):
0.200.1328 by Jelmer Vernooij
More test fixes.
306
        if self.mapping.is_special_file(path):
307
            return None
308
        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.
309
0.200.1569 by Jelmer Vernooij
Implement GitRevisionTree.all_file_ids().
310
    def all_file_ids(self):
311
        return set(self._fileid_map.all_file_ids())
312
0.200.1724 by Jelmer Vernooij
Add GitRevisionTree.all_versioned_paths implementation.
313
    def all_versioned_paths(self):
314
        ret = set()
0.400.1 by Jelmer Vernooij
Track store in Tree.all_versioned_paths.
315
        todo = set([(store, '', self.tree)])
0.200.1724 by Jelmer Vernooij
Add GitRevisionTree.all_versioned_paths implementation.
316
        while todo:
0.400.1 by Jelmer Vernooij
Track store in Tree.all_versioned_paths.
317
            (store, path, tree_id) = todo.pop()
0.287.2 by Jelmer Vernooij
Support empty trees in GitRevisionTree.
318
            if tree_id is None:
319
                continue
0.400.1 by Jelmer Vernooij
Track store in Tree.all_versioned_paths.
320
            tree = store[tree_id]
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
321
            for name, mode, hexsha in tree.items():
0.200.1724 by Jelmer Vernooij
Add GitRevisionTree.all_versioned_paths implementation.
322
                subpath = posixpath.join(path, name)
323
                if stat.S_ISDIR(mode):
0.400.1 by Jelmer Vernooij
Track store in Tree.all_versioned_paths.
324
                    todo.add((store, subpath, hexsha))
0.200.1724 by Jelmer Vernooij
Add GitRevisionTree.all_versioned_paths implementation.
325
                else:
326
                    ret.add(subpath)
327
        return ret
328
0.200.1204 by Jelmer Vernooij
Implement GitRevisionTree.get_root_id().
329
    def get_root_id(self):
0.378.1 by Jelmer Vernooij
Return None from Tree.get_root_id() when encountering an empty tree.
330
        if self.tree is None:
331
            return None
0.200.1204 by Jelmer Vernooij
Implement GitRevisionTree.get_root_id().
332
        return self.path2id("")
333
0.200.1208 by Jelmer Vernooij
Add GitWorkingTree.has_id and GitWorkingTree.has_or_had_id.
334
    def has_or_had_id(self, file_id):
0.372.1 by Jelmer Vernooij
Fix some missing id handling.
335
        try:
336
            path = self.id2path(file_id)
337
        except errors.NoSuchId:
338
            return False
339
        return True
0.200.1208 by Jelmer Vernooij
Add GitWorkingTree.has_id and GitWorkingTree.has_or_had_id.
340
341
    def has_id(self, file_id):
342
        try:
343
            path = self.id2path(file_id)
344
        except errors.NoSuchId:
345
            return False
346
        return self.has_filename(path)
347
0.381.1 by Jelmer Vernooij
Standardize looking up entries in Git trees.
348
    def _lookup_path(self, path):
349
        if self.tree is None:
350
            raise errors.NoSuchFile(path)
351
        try:
0.400.2 by Jelmer Vernooij
Return store from self._lookup_path.
352
            (mode, hexsha) = tree_lookup_path(self.store.__getitem__, self.tree,
0.381.1 by Jelmer Vernooij
Standardize looking up entries in Git trees.
353
                path.encode('utf-8'))
354
        except KeyError:
355
            raise errors.NoSuchFile(self, path)
0.400.2 by Jelmer Vernooij
Return store from self._lookup_path.
356
        else:
357
            return (self.store, mode, hexsha)
0.381.1 by Jelmer Vernooij
Standardize looking up entries in Git trees.
358
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
359
    def is_executable(self, path, file_id=None):
0.400.2 by Jelmer Vernooij
Return store from self._lookup_path.
360
        (store, mode, hexsha) = self._lookup_path(path)
0.200.1615 by Jelmer Vernooij
Implement GitRevisionTree.is_executable.:
361
        if mode is None:
362
            # the tree root is a directory
363
            return False
364
        return mode_is_executable(mode)
365
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
366
    def kind(self, path, file_id=None):
0.400.2 by Jelmer Vernooij
Return store from self._lookup_path.
367
        (store, mode, hexsha) = self._lookup_path(path)
0.200.1253 by Jelmer Vernooij
Fix Tree.kind(TREE_ROOT).
368
        if mode is None:
369
            # the tree root is a directory
370
            return "directory"
0.200.1241 by Jelmer Vernooij
Implement GitRevisionTree.kind.
371
        return mode_kind(mode)
372
0.200.1197 by Jelmer Vernooij
Implement GitRevisionTree.has_filename.
373
    def has_filename(self, path):
374
        try:
0.381.1 by Jelmer Vernooij
Standardize looking up entries in Git trees.
375
            self._lookup_path(path)
376
        except errors.NoSuchFile:
0.200.1197 by Jelmer Vernooij
Implement GitRevisionTree.has_filename.
377
            return False
378
        else:
379
            return True
380
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
381
    def list_files(self, include_root=False, from_dir=None, recursive=True):
0.381.1 by Jelmer Vernooij
Standardize looking up entries in Git trees.
382
        if self.tree is None:
383
            return
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
384
        if from_dir is None:
0.200.1197 by Jelmer Vernooij
Implement GitRevisionTree.has_filename.
385
            from_dir = u""
0.400.2 by Jelmer Vernooij
Return store from self._lookup_path.
386
        (store, mode, hexsha) = self._lookup_path(from_dir)
0.264.9 by Jelmer Vernooij
Implement basic GitWorkingTree.iter_entries_by_dir.
387
        if mode is None: # Root
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
388
            root_ie = self._get_dir_ie(b"", None)
0.264.9 by Jelmer Vernooij
Implement basic GitWorkingTree.iter_entries_by_dir.
389
        else:
0.264.10 by Jelmer Vernooij
Yield inventory entries.
390
            parent_path = posixpath.dirname(from_dir.encode("utf-8"))
0.200.1328 by Jelmer Vernooij
More test fixes.
391
            parent_id = self._fileid_map.lookup_file_id(parent_path)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
392
            if mode_kind(mode) == 'directory':
393
                root_ie = self._get_dir_ie(from_dir.encode("utf-8"), parent_id)
394
            else:
0.400.3 by Jelmer Vernooij
Pass store in a few more places.
395
                root_ie = self._get_file_ie(store, from_dir.encode("utf-8"),
0.264.10 by Jelmer Vernooij
Yield inventory entries.
396
                    posixpath.basename(from_dir), mode, hexsha)
397
        if from_dir != "" or include_root:
398
            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.
399
        todo = set()
0.264.10 by Jelmer Vernooij
Yield inventory entries.
400
        if root_ie.kind == 'directory':
0.400.2 by Jelmer Vernooij
Return store from self._lookup_path.
401
            todo.add((store, 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.
402
        while todo:
0.400.2 by Jelmer Vernooij
Return store from self._lookup_path.
403
            (store, path, hexsha, parent_id) = todo.pop()
404
            tree = store[hexsha]
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
405
            for name, mode, hexsha in tree.iteritems():
0.200.1328 by Jelmer Vernooij
More test fixes.
406
                if self.mapping.is_special_file(name):
407
                    continue
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
408
                child_path = posixpath.join(path, name)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
409
                if stat.S_ISDIR(mode):
410
                    ie = self._get_dir_ie(child_path, parent_id)
411
                    if recursive:
0.400.2 by Jelmer Vernooij
Return store from self._lookup_path.
412
                        todo.add((store, child_path, hexsha, ie.file_id))
0.264.10 by Jelmer Vernooij
Yield inventory entries.
413
                else:
0.400.3 by Jelmer Vernooij
Pass store in a few more places.
414
                    ie = self._get_file_ie(store, child_path, name, mode, hexsha, parent_id)
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
415
                yield child_path.decode('utf-8'), "V", ie.kind, ie.file_id, ie
0.264.10 by Jelmer Vernooij
Yield inventory entries.
416
0.400.3 by Jelmer Vernooij
Pass store in a few more places.
417
    def _get_file_ie(self, store, path, name, mode, hexsha, parent_id):
0.361.1 by Jelmer Vernooij
Don't use assert.
418
        if type(path) is not bytes:
419
            raise TypeError(path)
420
        if type(name) is not bytes:
421
            raise TypeError(name)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
422
        kind = mode_kind(mode)
0.200.1328 by Jelmer Vernooij
More test fixes.
423
        file_id = self._fileid_map.lookup_file_id(path)
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
424
        ie = entry_factory[kind](file_id, name.decode("utf-8"), parent_id)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
425
        if kind == 'symlink':
0.400.3 by Jelmer Vernooij
Pass store in a few more places.
426
            ie.symlink_target = store[hexsha].data.decode('utf-8')
0.200.1401 by Jelmer Vernooij
Cope with submodules in working trees.
427
        elif kind == 'tree-reference':
428
            ie.reference_revision = self.mapping.revision_id_foreign_to_bzr(hexsha)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
429
        else:
0.400.3 by Jelmer Vernooij
Pass store in a few more places.
430
            data = store[hexsha].data
0.264.10 by Jelmer Vernooij
Yield inventory entries.
431
            ie.text_sha1 = osutils.sha_string(data)
432
            ie.text_size = len(data)
433
            ie.executable = mode_is_executable(mode)
434
        return ie
435
436
    def _get_dir_ie(self, path, parent_id):
0.200.1328 by Jelmer Vernooij
More test fixes.
437
        file_id = self._fileid_map.lookup_file_id(path)
0.390.1 by Jelmer Vernooij
Don't set .revision for returned TreeEntry objects; it's slow, and callers can call Tree.get_file_revision if they really care.
438
        return GitTreeDirectory(file_id,
0.264.10 by Jelmer Vernooij
Yield inventory entries.
439
            posixpath.basename(path).decode("utf-8"), parent_id)
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
440
0.374.1 by Jelmer Vernooij
Implement GitRevisionTree.iter_child_entries.
441
    def iter_child_entries(self, path, file_id=None):
0.400.2 by Jelmer Vernooij
Return store from self._lookup_path.
442
        (store, mode, tree_sha) = self._lookup_path(path)
0.381.1 by Jelmer Vernooij
Standardize looking up entries in Git trees.
443
444
        if not stat.S_ISDIR(mode):
445
            return
446
0.374.1 by Jelmer Vernooij
Implement GitRevisionTree.iter_child_entries.
447
        encoded_path = path.encode('utf-8')
448
        file_id = self.path2id(path)
0.400.2 by Jelmer Vernooij
Return store from self._lookup_path.
449
        tree = store[tree_sha]
0.374.1 by Jelmer Vernooij
Implement GitRevisionTree.iter_child_entries.
450
        for name, mode, hexsha in tree.iteritems():
451
            if self.mapping.is_special_file(name):
452
                continue
453
            child_path = posixpath.join(encoded_path, name)
454
            if stat.S_ISDIR(mode):
455
                yield self._get_dir_ie(child_path, file_id)
456
            else:
0.400.3 by Jelmer Vernooij
Pass store in a few more places.
457
                yield self._get_file_ie(store, child_path, name, mode, hexsha,
0.374.1 by Jelmer Vernooij
Implement GitRevisionTree.iter_child_entries.
458
                                        file_id)
459
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
460
    def iter_entries_by_dir(self, specific_files=None, yield_parents=False):
0.287.4 by Jelmer Vernooij
Fix GitRevisionTree behaviour for null:
461
        if self.tree is None:
462
            return
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
463
        if yield_parents:
464
            # TODO(jelmer): Support yield parents
465
            raise NotImplementedError
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
466
        if specific_files is not None:
467
            if specific_files in ([""], []):
468
                specific_files = None
0.200.1285 by Jelmer Vernooij
Support specific_file_ids argument to Tree.iter_entries_by_dir.
469
            else:
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
470
                specific_files = set([p.encode('utf-8') for p in specific_files])
0.400.3 by Jelmer Vernooij
Pass store in a few more places.
471
        todo = set([(self.store, "", self.tree, None)])
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
472
        while todo:
0.400.3 by Jelmer Vernooij
Pass store in a few more places.
473
            store, path, tree_sha, parent_id = todo.pop()
0.264.10 by Jelmer Vernooij
Yield inventory entries.
474
            ie = self._get_dir_ie(path, parent_id)
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
475
            if specific_files is None or path in specific_files:
0.200.1715 by Jelmer Vernooij
Fix some more tests.
476
                yield path.decode("utf-8"), ie
0.400.3 by Jelmer Vernooij
Pass store in a few more places.
477
            tree = store[tree_sha]
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
478
            for name, mode, hexsha in tree.iteritems():
0.200.1328 by Jelmer Vernooij
More test fixes.
479
                if self.mapping.is_special_file(name):
480
                    continue
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
481
                child_path = posixpath.join(path, name)
482
                if stat.S_ISDIR(mode):
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
483
                    if (specific_files is None or
484
                        any(filter(lambda p: p.startswith(child_path), specific_files))):
0.400.3 by Jelmer Vernooij
Pass store in a few more places.
485
                        todo.add((store, child_path, hexsha, ie.file_id))
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
486
                elif specific_files is None or child_path in specific_files:
0.200.1715 by Jelmer Vernooij
Fix some more tests.
487
                    yield (child_path.decode("utf-8"),
0.400.3 by Jelmer Vernooij
Pass store in a few more places.
488
                            self._get_file_ie(store, child_path, name, mode, hexsha,
0.200.1285 by Jelmer Vernooij
Support specific_file_ids argument to Tree.iter_entries_by_dir.
489
                           ie.file_id))
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
490
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
491
    def get_revision_id(self):
0.200.959 by Jelmer Vernooij
Improve docstrings.
492
        """See RevisionTree.get_revision_id."""
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
493
        return self._revision_id
494
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
495
    def get_file_sha1(self, path, file_id=None, stat_value=None):
0.287.6 by Jelmer Vernooij
Fix some more tests.
496
        if self.tree is None:
497
            raise errors.NoSuchFile(path)
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
498
        return osutils.sha_string(self.get_file_text(path, file_id))
0.200.1255 by Jelmer Vernooij
Implement GitRevisionTree.get_file_sha1.
499
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
500
    def get_file_verifier(self, path, file_id=None, stat_value=None):
0.400.2 by Jelmer Vernooij
Return store from self._lookup_path.
501
        (store, mode, hexsha) = self._lookup_path(path)
0.200.1302 by Jelmer Vernooij
Significantly improve performance of WorkingTree.extras().
502
        return ("GIT", hexsha)
503
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
504
    def get_file_text(self, path, file_id=None):
0.200.959 by Jelmer Vernooij
Improve docstrings.
505
        """See RevisionTree.get_file_text."""
0.400.2 by Jelmer Vernooij
Return store from self._lookup_path.
506
        (store, mode, hexsha) = self._lookup_path(path)
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
507
        if stat.S_ISREG(mode):
0.400.2 by Jelmer Vernooij
Return store from self._lookup_path.
508
            return store[hexsha].data
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
509
        else:
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
510
            return b""
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
511
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
512
    def get_symlink_target(self, path, file_id=None):
0.200.1466 by Jelmer Vernooij
Implement GitRevisionTree.get_symlink_target.
513
        """See RevisionTree.get_symlink_target."""
0.400.2 by Jelmer Vernooij
Return store from self._lookup_path.
514
        (store, mode, hexsha) = self._lookup_path(path)
0.200.1466 by Jelmer Vernooij
Implement GitRevisionTree.get_symlink_target.
515
        if stat.S_ISLNK(mode):
0.400.2 by Jelmer Vernooij
Return store from self._lookup_path.
516
            return store[hexsha].data.decode('utf-8')
0.200.1466 by Jelmer Vernooij
Implement GitRevisionTree.get_symlink_target.
517
        else:
518
            return None
519
0.429.17 by Jelmer Vernooij
Fix some more tests.
520
    def get_reference_revision(self, path, file_id=None):
521
        """See RevisionTree.get_symlink_target."""
0.429.25 by Jelmer Vernooij
Merge trunk.
522
        (store, mode, hexsha) = self._lookup_path(path)
0.429.17 by Jelmer Vernooij
Fix some more tests.
523
        if S_ISGITLINK(mode):
0.429.26 by Jelmer Vernooij
Fix remaining test.
524
            nested_repo = self._get_nested_repository(path)
525
            return nested_repo.lookup_foreign_revision_id(hexsha)
0.429.17 by Jelmer Vernooij
Fix some more tests.
526
        else:
527
            return None
528
0.264.10 by Jelmer Vernooij
Yield inventory entries.
529
    def _comparison_data(self, entry, path):
530
        if entry is None:
531
            return None, False, None
532
        return entry.kind, entry.executable, None
533
0.200.1568 by Jelmer Vernooij
Implement GitRevisionTree.path_content_summary.
534
    def path_content_summary(self, path):
535
        """See Tree.path_content_summary."""
536
        try:
0.400.2 by Jelmer Vernooij
Return store from self._lookup_path.
537
            (store, mode, hexsha) = self._lookup_path(path)
0.381.1 by Jelmer Vernooij
Standardize looking up entries in Git trees.
538
        except errors.NoSuchFile:
0.200.1568 by Jelmer Vernooij
Implement GitRevisionTree.path_content_summary.
539
            return ('missing', None, None, None)
540
        kind = mode_kind(mode)
541
        if kind == 'file':
542
            executable = mode_is_executable(mode)
0.400.2 by Jelmer Vernooij
Return store from self._lookup_path.
543
            contents = store[hexsha].data
0.200.1568 by Jelmer Vernooij
Implement GitRevisionTree.path_content_summary.
544
            return (kind, len(contents), executable, osutils.sha_string(contents))
545
        elif kind == 'symlink':
0.400.2 by Jelmer Vernooij
Return store from self._lookup_path.
546
            return (kind, None, None, store[hexsha].data)
0.429.5 by Jelmer Vernooij
Fix tree_content_summary test.
547
        elif kind == 'tree-reference':
0.429.26 by Jelmer Vernooij
Fix remaining test.
548
            nested_repo = self._get_nested_repository(path)
0.429.5 by Jelmer Vernooij
Fix tree_content_summary test.
549
            return (kind, None, None,
0.429.26 by Jelmer Vernooij
Fix remaining test.
550
                    nested_repo.lookup_foreign_revision_id(hexsha))
0.200.1568 by Jelmer Vernooij
Implement GitRevisionTree.path_content_summary.
551
        else:
552
            return (kind, None, None, None)
553
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
554
    def find_related_paths_across_trees(self, paths, trees=[],
555
            require_versioned=True):
556
        if paths is None:
557
            return None
558
        if require_versioned:
559
            trees = [self] + (trees if trees is not None else [])
560
            unversioned = set()
561
            for p in paths:
562
                for t in trees:
563
                    if t.is_versioned(p):
564
                        break
565
                else:
566
                    unversioned.add(p)
567
            if unversioned:
568
                raise errors.PathsNotVersionedError(unversioned)
569
        return filter(self.is_versioned, paths)
570
0.393.1 by Jelmer Vernooij
Avoid expensive bzr APIs in commit.
571
    def _iter_tree_contents(self, include_trees=False):
572
        if self.tree is None:
573
            return iter([])
574
        return self.store.iter_tree_contents(
575
                self.tree, include_trees=include_trees)
576
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
577
0.252.43 by Jelmer Vernooij
Some refactoring, support proper file ids in revision deltas.
578
def tree_delta_from_git_changes(changes, mapping,
6964.2.1 by Jelmer Vernooij
Initial work to support brz-git on python3.
579
        fileid_maps, specific_files=None,
0.394.1 by Jelmer Vernooij
Fix reporting of extras in TreeDelta.
580
        require_versioned=False, include_root=False,
581
        target_extras=None):
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
582
    """Create a TreeDelta from two git trees.
0.200.959 by Jelmer Vernooij
Improve docstrings.
583
584
    source and target are iterators over tuples with:
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
585
        (filename, sha, mode)
586
    """
6964.2.1 by Jelmer Vernooij
Initial work to support brz-git on python3.
587
    (old_fileid_map, new_fileid_map) = fileid_maps
0.394.1 by Jelmer Vernooij
Fix reporting of extras in TreeDelta.
588
    if target_extras is None:
589
        target_extras = set()
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
590
    ret = delta.TreeDelta()
591
    for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes:
0.287.5 by Jelmer Vernooij
Fix root handling.
592
        if newpath == u'' and not include_root:
593
            continue
0.200.1743 by Jelmer Vernooij
Fix some revision delta filtering.
594
        if not (specific_files is None or
0.287.5 by Jelmer Vernooij
Fix root handling.
595
                (oldpath is not None and osutils.is_inside_or_parent_of_any(specific_files, oldpath)) or
596
                (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.
597
            continue
0.200.1752 by Jelmer Vernooij
Don't traverse nested trees in WorkingTree.smart_add.
598
        if mapping.is_special_file(oldpath):
0.252.43 by Jelmer Vernooij
Some refactoring, support proper file ids in revision deltas.
599
            oldpath = None
0.200.1752 by Jelmer Vernooij
Don't traverse nested trees in WorkingTree.smart_add.
600
        if mapping.is_special_file(newpath):
0.252.43 by Jelmer Vernooij
Some refactoring, support proper file ids in revision deltas.
601
            newpath = None
602
        if oldpath is None and newpath is None:
603
            continue
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
604
        if oldpath is None:
0.394.1 by Jelmer Vernooij
Fix reporting of extras in TreeDelta.
605
            if newpath in target_extras:
606
                ret.unversioned.append(
607
                    (osutils.normalized_filename(newpath)[0], None, mode_kind(newmode)))
608
            else:
609
                file_id = new_fileid_map.lookup_file_id(newpath)
610
                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.
611
        elif newpath is None:
0.200.1613 by Jelmer Vernooij
Handle encoding better in working tree iter changes.
612
            file_id = old_fileid_map.lookup_file_id(oldpath)
613
            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.
614
        elif oldpath != newpath:
0.200.1613 by Jelmer Vernooij
Handle encoding better in working tree iter changes.
615
            file_id = old_fileid_map.lookup_file_id(oldpath)
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
616
            ret.renamed.append(
617
                (oldpath.decode('utf-8'), newpath.decode('utf-8'), file_id,
618
                mode_kind(newmode), (oldsha != newsha),
619
                (oldmode != newmode)))
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
620
        elif mode_kind(oldmode) != mode_kind(newmode):
0.200.1613 by Jelmer Vernooij
Handle encoding better in working tree iter changes.
621
            file_id = new_fileid_map.lookup_file_id(newpath)
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
622
            ret.kind_changed.append(
623
                (newpath.decode('utf-8'), file_id, mode_kind(oldmode),
624
                mode_kind(newmode)))
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
625
        elif oldsha != newsha or oldmode != newmode:
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
626
            if stat.S_ISDIR(oldmode) and stat.S_ISDIR(newmode):
627
                continue
0.200.1613 by Jelmer Vernooij
Handle encoding better in working tree iter changes.
628
            file_id = new_fileid_map.lookup_file_id(newpath)
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
629
            ret.modified.append(
630
                (newpath.decode('utf-8'), file_id, mode_kind(newmode),
631
                (oldsha != newsha), (oldmode != newmode)))
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
632
        else:
0.200.1613 by Jelmer Vernooij
Handle encoding better in working tree iter changes.
633
            file_id = new_fileid_map.lookup_file_id(newpath)
634
            ret.unchanged.append((newpath.decode('utf-8'), file_id, mode_kind(newmode)))
0.394.1 by Jelmer Vernooij
Fix reporting of extras in TreeDelta.
635
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
636
    return ret
637
638
0.391.2 by Jelmer Vernooij
Pass along target_missing.
639
def changes_from_git_changes(changes, mapping, specific_files=None, include_unchanged=False,
0.391.7 by Jelmer Vernooij
Fix reporting of missing files in .iter_changes.
640
                             target_extras=None):
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
641
    """Create a iter_changes-like generator from a git stream.
0.200.959 by Jelmer Vernooij
Improve docstrings.
642
643
    source and target are iterators over tuples with:
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
644
        (filename, sha, mode)
645
    """
0.391.7 by Jelmer Vernooij
Fix reporting of missing files in .iter_changes.
646
    if target_extras is None:
647
        target_extras = set()
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
648
    for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes:
0.200.1743 by Jelmer Vernooij
Fix some revision delta filtering.
649
        if not (specific_files is None or
0.287.5 by Jelmer Vernooij
Fix root handling.
650
                (oldpath is not None and osutils.is_inside_or_parent_of_any(specific_files, oldpath)) or
651
                (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.
652
            continue
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
653
        path = (oldpath, newpath)
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
654
        if oldpath is not None and mapping.is_special_file(oldpath):
655
            continue
656
        if newpath is not None and mapping.is_special_file(newpath):
0.200.1328 by Jelmer Vernooij
More test fixes.
657
            continue
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
658
        if oldpath is None:
659
            fileid = mapping.generate_file_id(newpath)
660
            oldexe = None
661
            oldkind = None
662
            oldname = None
663
            oldparent = None
0.391.7 by Jelmer Vernooij
Fix reporting of missing files in .iter_changes.
664
            oldversioned = False
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
665
        else:
0.391.7 by Jelmer Vernooij
Fix reporting of missing files in .iter_changes.
666
            oldversioned = True
0.200.1345 by Jelmer Vernooij
paths should be unicode when yielded by iter_changes.
667
            oldpath = oldpath.decode("utf-8")
0.391.8 by Jelmer Vernooij
Allow missing items in old tree.
668
            if oldmode:
669
                oldexe = mode_is_executable(oldmode)
670
                oldkind = mode_kind(oldmode)
671
            else:
672
                oldexe = False
673
                oldkind = None
0.316.1 by Jelmer Vernooij
Fix iter_changes behaviour for trees in the tree root.
674
            if oldpath == u'':
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
675
                oldparent = None
0.316.1 by Jelmer Vernooij
Fix iter_changes behaviour for trees in the tree root.
676
                oldname = ''
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
677
            else:
0.316.1 by Jelmer Vernooij
Fix iter_changes behaviour for trees in the tree root.
678
                (oldparentpath, oldname) = osutils.split(oldpath)
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
679
                oldparent = mapping.generate_file_id(oldparentpath)
680
            fileid = mapping.generate_file_id(oldpath)
681
        if newpath is None:
682
            newexe = None
683
            newkind = None
684
            newname = None
685
            newparent = None
0.391.7 by Jelmer Vernooij
Fix reporting of missing files in .iter_changes.
686
            newversioned = False
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
687
        else:
0.391.7 by Jelmer Vernooij
Fix reporting of missing files in .iter_changes.
688
            newversioned = (newpath not in target_extras)
689
            if newmode:
0.200.1576 by Jelmer Vernooij
Merge a bunch of fixes from store-roundtrip-info.
690
                newexe = mode_is_executable(newmode)
691
                newkind = mode_kind(newmode)
692
            else:
693
                newexe = False
694
                newkind = None
0.391.7 by Jelmer Vernooij
Fix reporting of missing files in .iter_changes.
695
            newpath = newpath.decode("utf-8")
0.316.1 by Jelmer Vernooij
Fix iter_changes behaviour for trees in the tree root.
696
            if newpath == u'':
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
697
                newparent = None
0.316.1 by Jelmer Vernooij
Fix iter_changes behaviour for trees in the tree root.
698
                newname = u''
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
699
            else:
0.316.1 by Jelmer Vernooij
Fix iter_changes behaviour for trees in the tree root.
700
                newparentpath, newname = osutils.split(newpath)
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
701
                newparent = mapping.generate_file_id(newparentpath)
0.357.3 by Jelmer Vernooij
More remove fixes.
702
        if (not include_unchanged and
703
            oldkind == 'directory' and newkind == 'directory' and
704
            oldpath == newpath):
705
            continue
0.200.959 by Jelmer Vernooij
Improve docstrings.
706
        yield (fileid, (oldpath, newpath), (oldsha != newsha),
0.391.7 by Jelmer Vernooij
Fix reporting of missing files in .iter_changes.
707
             (oldversioned, newversioned),
0.200.959 by Jelmer Vernooij
Improve docstrings.
708
             (oldparent, newparent), (oldname, newname),
709
             (oldkind, newkind), (oldexe, newexe))
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
710
711
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
712
class InterGitTrees(_mod_tree.InterTree):
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
713
    """InterTree that works between two git trees."""
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
714
0.200.659 by Jelmer Vernooij
Prevent tests using InterGitRevisionTrees.
715
    _matching_from_tree_format = None
716
    _matching_to_tree_format = None
717
    _test_mutable_trees_to_test_trees = None
718
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
719
    @classmethod
720
    def is_compatible(cls, source, target):
0.200.959 by Jelmer Vernooij
Improve docstrings.
721
        return (isinstance(source, GitRevisionTree) and
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
722
                isinstance(target, GitRevisionTree))
723
724
    def compare(self, want_unchanged=False, specific_files=None,
725
                extra_trees=None, require_versioned=False, include_root=False,
726
                want_unversioned=False):
0.391.4 by Jelmer Vernooij
Simplify InterGitTrees implementation.
727
        with self.lock_read():
0.391.7 by Jelmer Vernooij
Fix reporting of missing files in .iter_changes.
728
            changes, target_extras = self._iter_git_changes(
0.391.4 by Jelmer Vernooij
Simplify InterGitTrees implementation.
729
                    want_unchanged=want_unchanged,
730
                    require_versioned=require_versioned,
731
                    specific_files=specific_files,
732
                    extra_trees=extra_trees,
733
                    want_unversioned=want_unversioned)
734
            source_fileid_map = self.source._fileid_map
735
            target_fileid_map = self.target._fileid_map
736
            return tree_delta_from_git_changes(changes, self.target.mapping,
737
                (source_fileid_map, target_fileid_map),
0.394.1 by Jelmer Vernooij
Fix reporting of extras in TreeDelta.
738
                specific_files=specific_files, include_root=include_root,
739
                target_extras=target_extras)
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
740
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
741
    def iter_changes(self, include_unchanged=False, specific_files=None,
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
742
                     pb=None, extra_trees=[], require_versioned=True,
743
                     want_unversioned=False):
0.391.4 by Jelmer Vernooij
Simplify InterGitTrees implementation.
744
        with self.lock_read():
0.391.7 by Jelmer Vernooij
Fix reporting of missing files in .iter_changes.
745
            changes, target_extras = self._iter_git_changes(
0.391.4 by Jelmer Vernooij
Simplify InterGitTrees implementation.
746
                    want_unchanged=include_unchanged,
747
                    require_versioned=require_versioned,
748
                    specific_files=specific_files,
749
                    extra_trees=extra_trees,
750
                    want_unversioned=want_unversioned)
751
            return changes_from_git_changes(
752
                    changes, self.target.mapping,
753
                    specific_files=specific_files,
754
                    include_unchanged=include_unchanged,
0.391.7 by Jelmer Vernooij
Fix reporting of missing files in .iter_changes.
755
                    target_extras=target_extras)
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
756
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
757
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
0.391.4 by Jelmer Vernooij
Simplify InterGitTrees implementation.
758
            require_versioned=False, extra_trees=None,
759
            want_unversioned=False):
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
760
        raise NotImplementedError(self._iter_git_changes)
761
762
763
class InterGitRevisionTrees(InterGitTrees):
764
    """InterTree that works between two git revision trees."""
765
766
    _matching_from_tree_format = None
767
    _matching_to_tree_format = None
768
    _test_mutable_trees_to_test_trees = None
769
770
    @classmethod
771
    def is_compatible(cls, source, target):
772
        return (isinstance(source, GitRevisionTree) and
773
                isinstance(target, GitRevisionTree))
774
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
775
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
0.391.4 by Jelmer Vernooij
Simplify InterGitTrees implementation.
776
            require_versioned=True, extra_trees=None,
777
            want_unversioned=False):
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
778
        trees = [self.source]
779
        if extra_trees is not None:
780
            trees.extend(extra_trees)
781
        if specific_files is not None:
782
            specific_files = self.target.find_related_paths_across_trees(
783
                    specific_files, trees,
784
                    require_versioned=require_versioned)
0.357.1 by Jelmer Vernooij
Fix some remove tests.
785
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
786
        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.
787
            store = OverlayObjectStore([self.source._repository._git.object_store,
788
                                        self.target._repository._git.object_store])
789
        else:
790
            store = self.source._repository._git.object_store
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
791
        return self.source._repository._git.object_store.tree_changes(
792
            self.source.tree, self.target.tree, want_unchanged=want_unchanged,
0.391.2 by Jelmer Vernooij
Pass along target_missing.
793
            include_trees=True, change_type_same=True), set()
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
794
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
795
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
796
_mod_tree.InterTree.register_optimiser(InterGitRevisionTrees)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
797
798
799
class MutableGitIndexTree(mutabletree.MutableTree):
800
801
    def __init__(self):
802
        self._lock_mode = None
803
        self._lock_count = 0
804
        self._versioned_dirs = None
0.415.1 by Jelmer Vernooij
Only write index when it's dirty.
805
        self._index_dirty = False
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
806
807
    def is_versioned(self, path):
808
        with self.lock_read():
809
            path = path.rstrip('/').encode('utf-8')
0.429.1 by Jelmer Vernooij
Abstract away index access.
810
            (index, subpath) = self._lookup_index(path)
811
            return (subpath in index or self._has_dir(path))
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
812
813
    def _has_dir(self, path):
814
        if path == "":
815
            return True
816
        if self._versioned_dirs is None:
817
            self._load_dirs()
818
        return path in self._versioned_dirs
819
820
    def _load_dirs(self):
0.361.3 by Jelmer Vernooij
Merge trunk,
821
        if self._lock_mode is None:
822
            raise errors.ObjectNotLocked(self)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
823
        self._versioned_dirs = set()
0.429.1 by Jelmer Vernooij
Abstract away index access.
824
        # TODO(jelmer): Browse over all indexes
0.429.11 by Jelmer Vernooij
Merge trunk.
825
        for p, i in self._recurse_index_entries():
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
826
            self._ensure_versioned_dir(posixpath.dirname(p))
827
828
    def _ensure_versioned_dir(self, dirname):
829
        if dirname in self._versioned_dirs:
830
            return
831
        if dirname != "":
832
            self._ensure_versioned_dir(posixpath.dirname(dirname))
833
        self._versioned_dirs.add(dirname)
834
835
    def path2id(self, path):
836
        with self.lock_read():
837
            path = path.rstrip('/')
838
            if self.is_versioned(path.rstrip('/')):
839
                return self._fileid_map.lookup_file_id(path.encode("utf-8"))
840
            return None
841
842
    def has_id(self, file_id):
843
        try:
844
            self.id2path(file_id)
845
        except errors.NoSuchId:
846
            return False
847
        else:
848
            return True
849
850
    def id2path(self, file_id):
851
        if file_id is None:
852
            return ''
0.361.3 by Jelmer Vernooij
Merge trunk,
853
        if type(file_id) is not bytes:
854
            raise TypeError(file_id)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
855
        with self.lock_read():
856
            try:
857
                path = self._fileid_map.lookup_path(file_id)
858
            except ValueError:
859
                raise errors.NoSuchId(self, file_id)
860
            path = path.decode('utf-8')
861
            if self.is_versioned(path):
862
                return path
863
            raise errors.NoSuchId(self, file_id)
864
865
    def _set_root_id(self, file_id):
866
        self._fileid_map.set_file_id("", file_id)
867
868
    def get_root_id(self):
869
        return self.path2id("")
870
871
    def _add(self, files, ids, kinds):
872
        for (path, file_id, kind) in zip(files, ids, kinds):
873
            if file_id is not None:
874
                raise workingtree.SettingFileIdUnsupported()
0.366.1 by Jelmer Vernooij
Fix normalized filename checking in add.
875
            path, can_access = osutils.normalized_filename(path)
876
            if not can_access:
877
                raise errors.InvalidNormalization(path)
6964.2.4 by Jelmer Vernooij
Fix running on python2.
878
            self._index_add_entry(path, kind)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
879
0.429.5 by Jelmer Vernooij
Fix tree_content_summary test.
880
    def _read_submodule_head(self, path):
881
        raise NotImplementedError(self._read_submodule_head)
882
0.429.1 by Jelmer Vernooij
Abstract away index access.
883
    def _lookup_index(self, encoded_path):
884
        if not isinstance(encoded_path, bytes):
885
            raise TypeError(encoded_path)
0.429.11 by Jelmer Vernooij
Merge trunk.
886
        # TODO(jelmer): Look in other indexes
0.429.1 by Jelmer Vernooij
Abstract away index access.
887
        return self.index, encoded_path
888
0.429.25 by Jelmer Vernooij
Merge trunk.
889
    def _index_del_entry(self, index, path):
890
        del index[path]
891
        # TODO(jelmer): Keep track of dirty per index
0.415.1 by Jelmer Vernooij
Only write index when it's dirty.
892
        self._index_dirty = True
893
0.429.16 by Jelmer Vernooij
Look at reference_revision on ie.
894
    def _index_add_entry(self, path, kind, flags=0, reference_revision=None):
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
895
        if kind == "directory":
896
            # Git indexes don't contain directories
897
            return
898
        if kind == "file":
899
            blob = Blob()
900
            try:
901
                file, stat_val = self.get_file_with_stat(path)
902
            except (errors.NoSuchFile, IOError):
903
                # TODO: Rather than come up with something here, use the old index
904
                file = BytesIO()
905
                stat_val = os.stat_result(
6964.2.1 by Jelmer Vernooij
Initial work to support brz-git on python3.
906
                    (stat.S_IFREG | 0o644, 0, 0, 0, 0, 0, 0, 0, 0, 0))
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
907
            blob.set_raw_string(file.read())
0.429.2 by Jelmer Vernooij
Some more work on submodule support.
908
            # Add object to the repository if it didn't exist yet
909
            if not blob.id in self.store:
910
                self.store.add_object(blob)
911
            hexsha = blob.id
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
912
        elif kind == "symlink":
913
            blob = Blob()
914
            try:
915
                stat_val = self._lstat(path)
0.429.2 by Jelmer Vernooij
Some more work on submodule support.
916
            except EnvironmentError:
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
917
                # TODO: Rather than come up with something here, use the
918
                # old index
919
                stat_val = os.stat_result(
920
                    (stat.S_IFLNK, 0, 0, 0, 0, 0, 0, 0, 0, 0))
921
            blob.set_raw_string(
922
                self.get_symlink_target(path).encode("utf-8"))
0.429.2 by Jelmer Vernooij
Some more work on submodule support.
923
            # Add object to the repository if it didn't exist yet
924
            if not blob.id in self.store:
925
                self.store.add_object(blob)
926
            hexsha = blob.id
927
        elif kind == "tree-reference":
0.429.16 by Jelmer Vernooij
Look at reference_revision on ie.
928
            if reference_revision is not None:
929
                hexsha = self.branch.lookup_bzr_revision_id(reference_revision)[0]
930
            else:
931
                hexsha = self._read_submodule_head(path)
932
                if hexsha is None:
933
                    raise errors.NoCommits(path)
0.429.2 by Jelmer Vernooij
Some more work on submodule support.
934
            try:
935
                stat_val = self._lstat(path)
936
            except EnvironmentError:
937
                stat_val = os.stat_result(
938
                    (S_IFGITLINK, 0, 0, 0, 0, 0, 0, 0, 0, 0))
939
            stat_val = os.stat_result((S_IFGITLINK, ) + stat_val[1:])
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
940
        else:
941
            raise AssertionError("unknown kind '%s'" % kind)
942
        # Add an entry to the index or update the existing entry
943
        ensure_normalized_path(path)
944
        encoded_path = path.encode("utf-8")
945
        if b'\r' in encoded_path or b'\n' in encoded_path:
946
            # TODO(jelmer): Why do we need to do this?
947
            trace.mutter('ignoring path with invalid newline in it: %r', path)
948
            return
0.429.1 by Jelmer Vernooij
Abstract away index access.
949
        (index, index_path) = self._lookup_index(encoded_path)
0.429.2 by Jelmer Vernooij
Some more work on submodule support.
950
        index[index_path] = index_entry_from_stat(stat_val, hexsha, flags)
0.415.1 by Jelmer Vernooij
Only write index when it's dirty.
951
        self._index_dirty = True
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
952
        if self._versioned_dirs is not None:
0.429.1 by Jelmer Vernooij
Abstract away index access.
953
            self._ensure_versioned_dir(index_path)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
954
0.429.7 by Jelmer Vernooij
Consistent file ids.
955
    def _recurse_index_entries(self, index=None, basepath=""):
956
        # Iterate over all index entries
957
        with self.lock_read():
958
            if index is None:
959
                index = self.index
960
            for path, value in index.iteritems():
961
                yield (posixpath.join(basepath, path), value)
0.429.10 by Jelmer Vernooij
use new read_submodule_head from dulwich.
962
                (ctime, mtime, dev, ino, mode, uid, gid, size, sha, flags) = value
963
                if S_ISGITLINK(mode):
0.429.7 by Jelmer Vernooij
Consistent file ids.
964
                    pass # TODO(jelmer): dive into submodule
965
966
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
967
    def iter_entries_by_dir(self, specific_files=None, yield_parents=False):
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
968
        if yield_parents:
969
            raise NotImplementedError(self.iter_entries_by_dir)
970
        with self.lock_read():
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
971
            if specific_files is not None:
972
                specific_files = set(specific_files)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
973
            else:
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
974
                specific_files = None
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
975
            root_ie = self._get_dir_ie(u"", None)
976
            ret = {}
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
977
            if specific_files is None or u"" in specific_files:
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
978
                ret[(None, u"")] = root_ie
979
            dir_ids = {u"": root_ie.file_id}
0.429.7 by Jelmer Vernooij
Consistent file ids.
980
            for path, value in self._recurse_index_entries():
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
981
                if self.mapping.is_special_file(path):
982
                    continue
983
                path = path.decode("utf-8")
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
984
                if specific_files is not None and not path in specific_files:
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
985
                    continue
986
                (parent, name) = posixpath.split(path)
987
                try:
988
                    file_ie = self._get_file_ie(name, path, value, None)
989
                except errors.NoSuchFile:
990
                    continue
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
991
                if yield_parents or specific_files is None:
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
992
                    for (dir_path, dir_ie) in self._add_missing_parent_ids(parent,
993
                            dir_ids):
994
                        ret[(posixpath.dirname(dir_path), dir_path)] = dir_ie
0.429.12 by Jelmer Vernooij
Remove revision.
995
                file_ie.parent_id = self.path2id(parent)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
996
                ret[(posixpath.dirname(path), path)] = file_ie
997
            return ((path, ie) for ((_, path), ie) in sorted(ret.items()))
998
0.429.7 by Jelmer Vernooij
Consistent file ids.
999
    def iter_references(self):
1000
        # TODO(jelmer): Implement a more efficient version of this
1001
        for path, entry in self.iter_entries_by_dir():
1002
            if entry.kind == 'tree-reference':
0.429.20 by Jelmer Vernooij
Fix iter_references.
1003
                yield path, self.mapping.generate_file_id(b'')
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
1004
1005
    def _get_dir_ie(self, path, parent_id):
1006
        file_id = self.path2id(path)
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
1007
        return GitTreeDirectory(file_id,
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
1008
            posixpath.basename(path).strip("/"), parent_id)
1009
1010
    def _get_file_ie(self, name, path, value, parent_id):
0.361.3 by Jelmer Vernooij
Merge trunk,
1011
        if type(name) is not unicode:
1012
            raise TypeError(name)
1013
        if type(path) is not unicode:
1014
            raise TypeError(path)
1015
        if not isinstance(value, tuple) or len(value) != 10:
1016
            raise TypeError(value)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
1017
        (ctime, mtime, dev, ino, mode, uid, gid, size, sha, flags) = value
1018
        file_id = self.path2id(path)
1019
        if type(file_id) != str:
1020
            raise AssertionError
1021
        kind = mode_kind(mode)
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
1022
        ie = entry_factory[kind](file_id, name, parent_id)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
1023
        if kind == 'symlink':
1024
            ie.symlink_target = self.get_symlink_target(path, file_id)
0.429.7 by Jelmer Vernooij
Consistent file ids.
1025
        elif kind == 'tree-reference':
1026
            ie.reference_revision = self.get_reference_revision(path, file_id)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
1027
        else:
1028
            try:
1029
                data = self.get_file_text(path, file_id)
1030
            except errors.NoSuchFile:
1031
                data = None
1032
            except IOError as e:
1033
                if e.errno != errno.ENOENT:
1034
                    raise
1035
                data = None
1036
            if data is None:
1037
                data = self.branch.repository._git.object_store[sha].data
1038
            ie.text_sha1 = osutils.sha_string(data)
1039
            ie.text_size = len(data)
1040
            ie.executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1041
        return ie
1042
1043
    def _add_missing_parent_ids(self, path, dir_ids):
1044
        if path in dir_ids:
1045
            return []
1046
        parent = posixpath.dirname(path).strip("/")
1047
        ret = self._add_missing_parent_ids(parent, dir_ids)
1048
        parent_id = dir_ids[parent]
1049
        ie = self._get_dir_ie(path, parent_id)
1050
        dir_ids[path] = ie.file_id
1051
        ret.append((path, ie))
1052
        return ret
1053
1054
    def _comparison_data(self, entry, path):
1055
        if entry is None:
1056
            return None, False, None
1057
        return entry.kind, entry.executable, None
1058
1059
    def _unversion_path(self, path):
0.361.3 by Jelmer Vernooij
Merge trunk,
1060
        if self._lock_mode is None:
1061
            raise errors.ObjectNotLocked(self)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
1062
        encoded_path = path.encode("utf-8")
1063
        count = 0
0.429.1 by Jelmer Vernooij
Abstract away index access.
1064
        (index, subpath) = self._lookup_index(encoded_path)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
1065
        try:
0.429.25 by Jelmer Vernooij
Merge trunk.
1066
            self._index_del_entry(index, encoded_path)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
1067
        except KeyError:
1068
            # A directory, perhaps?
0.429.1 by Jelmer Vernooij
Abstract away index access.
1069
            # TODO(jelmer): Deletes that involve submodules?
1070
            for p in list(index):
1071
                if p.startswith(subpath+b"/"):
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
1072
                    count += 1
0.429.25 by Jelmer Vernooij
Merge trunk.
1073
                    self._index_del_entry(index, p)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
1074
        else:
1075
            count = 1
1076
        self._versioned_dirs = None
1077
        return count
1078
1079
    def unversion(self, paths, file_ids=None):
1080
        with self.lock_tree_write():
1081
            for path in paths:
1082
                if self._unversion_path(path) == 0:
1083
                    raise errors.NoSuchFile(path)
1084
            self._versioned_dirs = None
1085
            self.flush()
1086
1087
    def flush(self):
1088
        pass
1089
1090
    def update_basis_by_delta(self, revid, delta):
1091
        # TODO(jelmer): This shouldn't be called, it's inventory specific.
1092
        for (old_path, new_path, file_id, ie) in delta:
0.429.1 by Jelmer Vernooij
Abstract away index access.
1093
            if old_path is not None:
1094
                (index, old_subpath) = self._lookup_index(old_path.encode('utf-8'))
1095
                if old_subpath in index:
0.429.25 by Jelmer Vernooij
Merge trunk.
1096
                    self._index_del_entry(index, old_subpath)
0.429.1 by Jelmer Vernooij
Abstract away index access.
1097
                    self._versioned_dirs = None
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
1098
            if new_path is not None and ie.kind != 'directory':
6964.2.4 by Jelmer Vernooij
Fix running on python2.
1099
                self._index_add_entry(new_path, ie.kind)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
1100
        self.flush()
1101
        self._set_merges_from_parent_ids([])
0.360.4 by Jelmer Vernooij
Implement MemoryTree.rename_one, MemoryTree.mkdir.
1102
1103
    def move(self, from_paths, to_dir=None, after=None):
1104
        rename_tuples = []
1105
        with self.lock_tree_write():
1106
            to_abs = self.abspath(to_dir)
1107
            if not os.path.isdir(to_abs):
1108
                raise errors.BzrMoveFailedError('', to_dir,
1109
                    errors.NotADirectory(to_abs))
1110
1111
            for from_rel in from_paths:
1112
                from_tail = os.path.split(from_rel)[-1]
1113
                to_rel = os.path.join(to_dir, from_tail)
1114
                self.rename_one(from_rel, to_rel, after=after)
1115
                rename_tuples.append((from_rel, to_rel))
1116
            self.flush()
1117
            return rename_tuples
1118
1119
    def rename_one(self, from_rel, to_rel, after=None):
1120
        from_path = from_rel.encode("utf-8")
1121
        to_rel, can_access = osutils.normalized_filename(to_rel)
1122
        if not can_access:
1123
            raise errors.InvalidNormalization(to_rel)
1124
        to_path = to_rel.encode("utf-8")
1125
        with self.lock_tree_write():
1126
            if not after:
1127
                # Perhaps it's already moved?
1128
                after = (
1129
                    not self.has_filename(from_rel) and
1130
                    self.has_filename(to_rel) and
1131
                    not self.is_versioned(to_rel))
1132
            if after:
1133
                if not self.has_filename(to_rel):
1134
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
1135
                        errors.NoSuchFile(to_rel))
1136
                if self.basis_tree().is_versioned(to_rel):
1137
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
1138
                        errors.AlreadyVersionedError(to_rel))
1139
1140
                kind = self.kind(to_rel)
1141
            else:
1142
                try:
1143
                    to_kind = self.kind(to_rel)
1144
                except errors.NoSuchFile:
1145
                    exc_type = errors.BzrRenameFailedError
1146
                    to_kind = None
1147
                else:
1148
                    exc_type = errors.BzrMoveFailedError
1149
                if self.is_versioned(to_rel):
1150
                    raise exc_type(from_rel, to_rel,
1151
                        errors.AlreadyVersionedError(to_rel))
1152
                if not self.has_filename(from_rel):
1153
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
1154
                        errors.NoSuchFile(from_rel))
0.388.1 by Jelmer Vernooij
Don't print error moving to an unversioned directory.
1155
                kind = self.kind(from_rel)
1156
                if not self.is_versioned(from_rel) and kind != 'directory':
0.360.4 by Jelmer Vernooij
Implement MemoryTree.rename_one, MemoryTree.mkdir.
1157
                    raise exc_type(from_rel, to_rel,
1158
                        errors.NotVersionedError(from_rel))
1159
                if self.has_filename(to_rel):
1160
                    raise errors.RenameFailedFilesExist(
1161
                        from_rel, to_rel, errors.FileExists(to_rel))
1162
1163
                kind = self.kind(from_rel)
1164
0.429.1 by Jelmer Vernooij
Abstract away index access.
1165
            if not after and kind != 'directory':
1166
                (index, from_subpath) = self._lookup_index(from_path)
1167
                if from_subpath not in index:
1168
                    # It's not a file
1169
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
1170
                        errors.NotVersionedError(path=from_rel))
0.360.4 by Jelmer Vernooij
Implement MemoryTree.rename_one, MemoryTree.mkdir.
1171
1172
            if not after:
1173
                try:
1174
                    self._rename_one(from_rel, to_rel)
1175
                except OSError as e:
1176
                    if e.errno == errno.ENOENT:
1177
                        raise errors.BzrMoveFailedError(from_rel, to_rel,
1178
                            errors.NoSuchFile(to_rel))
1179
                    raise
1180
            if kind != 'directory':
0.429.13 by Jelmer Vernooij
Fix regressions.
1181
                (index, from_index_path) = self._lookup_index(from_path)
0.360.4 by Jelmer Vernooij
Implement MemoryTree.rename_one, MemoryTree.mkdir.
1182
                try:
0.429.25 by Jelmer Vernooij
Merge trunk.
1183
                    self._index_del_entry(index, from_path)
0.360.4 by Jelmer Vernooij
Implement MemoryTree.rename_one, MemoryTree.mkdir.
1184
                except KeyError:
1185
                    pass
1186
                self._index_add_entry(to_rel, kind)
1187
            else:
0.429.11 by Jelmer Vernooij
Merge trunk.
1188
                todo = [(p, i) for (p, i) in self._recurse_index_entries() if p.startswith(from_path+'/')]
0.429.13 by Jelmer Vernooij
Fix regressions.
1189
                for child_path, child_value in todo:
6973.1.1 by Jelmer Vernooij
Make InterIndexGitTree suitable for use with MemoryGitTree.
1190
                    (child_to_index, child_to_index_path) = self._lookup_index(
1191
                            posixpath.join(to_path, posixpath.relpath(child_path, from_path)))
0.429.13 by Jelmer Vernooij
Fix regressions.
1192
                    child_to_index[child_to_index_path] = child_value
0.429.25 by Jelmer Vernooij
Merge trunk.
1193
                    # TODO(jelmer): Mark individual index as dirty
0.415.1 by Jelmer Vernooij
Only write index when it's dirty.
1194
                    self._index_dirty = True
0.429.13 by Jelmer Vernooij
Fix regressions.
1195
                    (child_from_index, child_from_index_path) = self._lookup_index(child_path)
0.429.25 by Jelmer Vernooij
Merge trunk.
1196
                    self._index_del_entry(child_from_index, child_from_index_path)
0.360.4 by Jelmer Vernooij
Implement MemoryTree.rename_one, MemoryTree.mkdir.
1197
1198
            self._versioned_dirs = None
1199
            self.flush()
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
1200
1201
    def find_related_paths_across_trees(self, paths, trees=[],
1202
            require_versioned=True):
1203
        if paths is None:
1204
            return None
1205
1206
        if require_versioned:
1207
            trees = [self] + (trees if trees is not None else [])
1208
            unversioned = set()
1209
            for p in paths:
1210
                for t in trees:
1211
                    if t.is_versioned(p):
1212
                        break
1213
                else:
1214
                    unversioned.add(p)
1215
            if unversioned:
1216
                raise errors.PathsNotVersionedError(unversioned)
1217
1218
        return filter(self.is_versioned, paths)
0.429.5 by Jelmer Vernooij
Fix tree_content_summary test.
1219
1220
    def path_content_summary(self, path):
1221
        """See Tree.path_content_summary."""
1222
        try:
1223
            stat_result = self._lstat(path)
1224
        except OSError as e:
1225
            if getattr(e, 'errno', None) == errno.ENOENT:
1226
                # no file.
1227
                return ('missing', None, None, None)
1228
            # propagate other errors
1229
            raise
1230
        kind = mode_kind(stat_result.st_mode)
1231
        if kind == 'file':
1232
            return self._file_content_summary(path, stat_result)
1233
        elif kind == 'directory':
1234
            # perhaps it looks like a plain directory, but it's really a
1235
            # reference.
1236
            if self._directory_is_tree_reference(path):
1237
                kind = 'tree-reference'
1238
            return kind, None, None, None
1239
        elif kind == 'symlink':
1240
            target = osutils.readlink(self.abspath(path))
1241
            return ('symlink', None, None, target)
1242
        else:
1243
            return (kind, None, None, None)
1244
1245
    def kind(self, relpath, file_id=None):
0.429.15 by Jelmer Vernooij
Autodetect tree-reference based on index.
1246
        kind = osutils.file_kind(self.abspath(relpath))
1247
        if kind == 'directory':
1248
            (index, index_path) = self._lookup_index(relpath.encode('utf-8'))
1249
            try:
1250
                mode = index[index_path].mode
1251
            except KeyError:
1252
                return kind
1253
            else:
1254
                if S_ISGITLINK(mode):
1255
                    return 'tree-reference'
1256
                return 'directory'
1257
        else:
1258
            return kind
6973.1.1 by Jelmer Vernooij
Make InterIndexGitTree suitable for use with MemoryGitTree.
1259
1260
    def _live_entry(self, relpath):
1261
        raise NotImplementedError(self._live_entry)
1262
1263
1264
class InterIndexGitTree(InterGitTrees):
1265
    """InterTree that works between a Git revision tree and an index."""
1266
1267
    def __init__(self, source, target):
1268
        super(InterIndexGitTree, self).__init__(source, target)
1269
        self._index = target.index
1270
1271
    @classmethod
1272
    def is_compatible(cls, source, target):
1273
        return (isinstance(source, GitRevisionTree) and
1274
                isinstance(target, MutableGitIndexTree))
1275
1276
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
1277
            require_versioned=False, extra_trees=None,
1278
            want_unversioned=False):
1279
        trees = [self.source]
1280
        if extra_trees is not None:
1281
            trees.extend(extra_trees)
1282
        if specific_files is not None:
1283
            specific_files = self.target.find_related_paths_across_trees(
1284
                    specific_files, trees,
1285
                    require_versioned=require_versioned)
1286
        # TODO(jelmer): Restrict to specific_files, for performance reasons.
1287
        with self.lock_read():
1288
            return changes_between_git_tree_and_working_copy(
1289
                self.source.store, self.source.tree,
1290
                self.target, want_unchanged=want_unchanged,
1291
                want_unversioned=want_unversioned)
1292
1293
1294
_mod_tree.InterTree.register_optimiser(InterIndexGitTree)
1295
1296
1297
def changes_between_git_tree_and_working_copy(store, from_tree_sha, target,
1298
        want_unchanged=False, want_unversioned=False):
1299
    """Determine the changes between a git tree and a working tree with index.
1300
1301
    """
1302
    extras = set()
1303
    blobs = {}
1304
    # Report dirified directories to commit_tree first, so that they can be
1305
    # replaced with non-empty directories if they have contents.
1306
    dirified = []
1307
    for path, index_entry in target._recurse_index_entries():
1308
        try:
1309
            live_entry = target._live_entry(path)
1310
        except EnvironmentError as e:
1311
            if e.errno == errno.ENOENT:
1312
                # Entry was removed; keep it listed, but mark it as gone.
1313
                blobs[path] = (ZERO_SHA, 0)
1314
            elif e.errno == errno.EISDIR:
1315
                # Entry was turned into a directory
1316
                dirified.append((path, Tree().id, stat.S_IFDIR))
1317
                store.add_object(Tree())
1318
            else:
1319
                raise
1320
        else:
1321
            blobs[path] = (live_entry.sha, cleanup_mode(live_entry.mode))
1322
    if want_unversioned:
1323
        for e in target.extras():
1324
            st = target._lstat(e)
1325
            try:
1326
                np, accessible = osutils.normalized_filename(e)
1327
            except UnicodeDecodeError:
1328
                raise errors.BadFilenameEncoding(
1329
                    e, osutils._fs_enc)
1330
            if stat.S_ISDIR(st.st_mode):
1331
                blob = Tree()
1332
            else:
1333
                blob = blob_from_path_and_stat(target.abspath(e).encode(osutils._fs_enc), st)
1334
            store.add_object(blob)
1335
            np = np.encode('utf-8')
1336
            blobs[np] = (blob.id, cleanup_mode(st.st_mode))
1337
            extras.add(np)
1338
    to_tree_sha = commit_tree(store, dirified + [(p, s, m) for (p, (s, m)) in blobs.items()])
1339
    return store.tree_changes(
1340
        from_tree_sha, to_tree_sha, include_trees=True,
1341
        want_unchanged=want_unchanged, change_type_same=True), extras