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