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