/brz/remove-bazaar

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