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