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