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