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