/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 (
27
    index_entry_from_stat,
28
    )
0.200.1780 by Jelmer Vernooij
Fix cross-object-store tree comparison.
29
from dulwich.object_store import (
30
    tree_lookup_path,
31
    OverlayObjectStore,
32
    )
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
33
from dulwich.objects import (
34
    Blob,
35
    Tree,
0.372.1 by Jelmer Vernooij
Fix some missing id handling.
36
    ZERO_SHA,
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
37
    )
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
38
import stat
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
39
import posixpath
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
40
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
41
from ... import (
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
42
    delta,
43
    errors,
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
44
    lock,
45
    mutabletree,
0.264.10 by Jelmer Vernooij
Yield inventory entries.
46
    osutils,
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
47
    revisiontree,
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
48
    trace,
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
49
    tree as _mod_tree,
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
50
    workingtree,
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
51
    )
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
52
from ...revision import NULL_REVISION
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
53
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
54
from .mapping import (
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
55
    mode_is_executable,
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
56
    mode_kind,
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
57
    GitFileIdMap,
0.287.2 by Jelmer Vernooij
Support empty trees in GitRevisionTree.
58
    default_mapping,
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
59
    )
60
61
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
62
class GitTreeDirectory(_mod_tree.TreeDirectory):
63
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.
64
    __slots__ = ['file_id', 'name', 'parent_id', 'children']
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
65
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.
66
    def __init__(self, file_id, name, parent_id):
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
67
        self.file_id = file_id
68
        self.name = name
69
        self.parent_id = parent_id
70
        # TODO(jelmer)
71
        self.children = {}
72
73
    @property
74
    def kind(self):
75
        return 'directory'
76
77
    @property
78
    def executable(self):
79
        return False
80
81
    def copy(self):
82
        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.
83
            self.file_id, self.name, self.parent_id)
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
84
85
    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.
86
        return "%s(file_id=%r, name=%r, parent_id=%r)" % (
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
87
            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.
88
            self.parent_id)
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
89
90
    def __eq__(self, other):
91
        return (self.kind == other.kind and
92
                self.file_id == other.file_id and
93
                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.
94
                self.parent_id == other.parent_id)
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
95
96
97
class GitTreeFile(_mod_tree.TreeFile):
98
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
    __slots__ = ['file_id', 'name', 'parent_id', 'text_size', 'text_sha1',
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
100
                 'executable']
101
0.390.2 by Jelmer Vernooij
merge trunk
102
    def __init__(self, file_id, name, parent_id, text_size=None,
0.389.2 by Jelmer Vernooij
Add repr implementation to GitTreeFile & GitTreeLink.
103
                 text_sha1=None, executable=None):
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
104
        self.file_id = file_id
105
        self.name = name
106
        self.parent_id = parent_id
0.389.2 by Jelmer Vernooij
Add repr implementation to GitTreeFile & GitTreeLink.
107
        self.text_size = text_size
108
        self.text_sha1 = text_sha1
109
        self.executable = executable
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
110
111
    @property
112
    def kind(self):
113
        return 'file'
114
115
    def __eq__(self, other):
116
        return (self.kind == other.kind and
117
                self.file_id == other.file_id and
118
                self.name == other.name and
119
                self.parent_id == other.parent_id and
120
                self.text_sha1 == other.text_sha1 and
121
                self.text_size == other.text_size and
122
                self.executable == other.executable)
123
0.389.2 by Jelmer Vernooij
Add repr implementation to GitTreeFile & GitTreeLink.
124
    def __repr__(self):
0.390.2 by Jelmer Vernooij
merge trunk
125
        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.
126
            type(self).__name__, self.file_id, self.name, self.parent_id,
0.390.2 by Jelmer Vernooij
merge trunk
127
            self.text_size, self.text_sha1, self.executable)
0.389.2 by Jelmer Vernooij
Add repr implementation to GitTreeFile & GitTreeLink.
128
0.365.2 by Jelmer Vernooij
Add eq/copy.
129
    def copy(self):
130
        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.
131
                self.file_id, self.name, self.parent_id)
0.365.2 by Jelmer Vernooij
Add eq/copy.
132
        ret.text_sha1 = self.text_sha1
133
        ret.text_size = self.text_size
134
        ret.executable = self.executable
135
        return ret
136
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
137
138
class GitTreeSymlink(_mod_tree.TreeLink):
139
0.390.1 by Jelmer Vernooij
Don't set .revision for returned TreeEntry objects; it's slow, and callers can call Tree.get_file_revision if they really care.
140
    __slots__ = ['file_id', 'name', 'parent_id', 'symlink_target']
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
141
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.
142
    def __init__(self, file_id, name, parent_id,
0.365.2 by Jelmer Vernooij
Add eq/copy.
143
                 symlink_target=None):
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
144
        self.file_id = file_id
145
        self.name = name
146
        self.parent_id = parent_id
0.365.2 by Jelmer Vernooij
Add eq/copy.
147
        self.symlink_target = symlink_target
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
148
149
    @property
150
    def kind(self):
151
        return 'symlink'
152
153
    @property
154
    def executable(self):
155
        return False
156
157
    @property
158
    def text_size(self):
159
        return None
160
0.389.2 by Jelmer Vernooij
Add repr implementation to GitTreeFile & GitTreeLink.
161
    def __repr__(self):
0.390.2 by Jelmer Vernooij
merge trunk
162
        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.
163
            type(self).__name__, self.file_id, self.name, self.parent_id,
0.390.2 by Jelmer Vernooij
merge trunk
164
            self.symlink_target)
0.389.2 by Jelmer Vernooij
Add repr implementation to GitTreeFile & GitTreeLink.
165
0.365.2 by Jelmer Vernooij
Add eq/copy.
166
    def __eq__(self, other):
167
        return (self.kind == other.kind and
168
                self.file_id == other.file_id and
169
                self.name == other.name and
170
                self.parent_id == other.parent_id and
171
                self.symlink_target == other.symlink_target)
172
173
    def copy(self):
174
        return self.__class__(
175
                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.
176
                self.symlink_target)
0.365.2 by Jelmer Vernooij
Add eq/copy.
177
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
178
179
entry_factory = {
180
    'directory': GitTreeDirectory,
181
    'file': GitTreeFile,
182
    'symlink': GitTreeSymlink,
183
    }
184
185
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
186
def ensure_normalized_path(path):
187
    """Check whether path is normalized.
188
189
    :raises InvalidNormalization: When path is not normalized, and cannot be
190
        accessed on this platform by the normalized path.
191
    :return: The NFC normalised version of path.
192
    """
193
    norm_path, can_access = osutils.normalized_filename(path)
194
    if norm_path != path:
195
        if can_access:
196
            return norm_path
197
        else:
198
            raise errors.InvalidNormalization(path)
199
    return path
200
201
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
202
class GitRevisionTree(revisiontree.RevisionTree):
0.200.959 by Jelmer Vernooij
Improve docstrings.
203
    """Revision tree implementation based on Git objects."""
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
204
205
    def __init__(self, repository, revision_id):
206
        self._revision_id = revision_id
207
        self._repository = repository
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
208
        self.store = repository._git.object_store
0.361.1 by Jelmer Vernooij
Don't use assert.
209
        if type(revision_id) is not str:
210
            raise TypeError(revision_id)
0.200.1283 by Jelmer Vernooij
Provide Repository.get_file_graph() and Tree.get_file_revision().
211
        self.commit_id, self.mapping = repository.lookup_bzr_revision_id(revision_id)
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
212
        if revision_id == NULL_REVISION:
213
            self.tree = None
0.287.2 by Jelmer Vernooij
Support empty trees in GitRevisionTree.
214
            self.mapping = default_mapping
215
            self._fileid_map = GitFileIdMap(
0.287.4 by Jelmer Vernooij
Fix GitRevisionTree behaviour for null:
216
                {},
0.287.2 by Jelmer Vernooij
Support empty trees in GitRevisionTree.
217
                default_mapping)
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
218
        else:
219
            try:
220
                commit = self.store[self.commit_id]
221
            except KeyError, r:
222
                raise errors.NoSuchRevision(repository, revision_id)
223
            self.tree = commit.tree
224
            self._fileid_map = self.mapping.get_fileid_map(self.store.__getitem__, self.tree)
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
225
0.349.1 by Jelmer Vernooij
Support supports_rename_tracking method.
226
    def supports_rename_tracking(self):
227
        return False
228
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
229
    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().
230
        change_scanner = self._repository._file_change_scanner
0.372.1 by Jelmer Vernooij
Fix some missing id handling.
231
        if self.commit_id == ZERO_SHA:
232
            return NULL_REVISION
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
233
        (path, commit_id) = change_scanner.find_last_change_revision(
234
            path.encode('utf-8'), self.commit_id)
0.200.1283 by Jelmer Vernooij
Provide Repository.get_file_graph() and Tree.get_file_revision().
235
        return self._repository.lookup_foreign_revision_id(commit_id, self.mapping)
236
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
237
    def get_file_mtime(self, path, file_id=None):
238
        revid = self.get_file_revision(path, file_id)
0.200.1283 by Jelmer Vernooij
Provide Repository.get_file_graph() and Tree.get_file_revision().
239
        try:
240
            rev = self._repository.get_revision(revid)
241
        except errors.NoSuchRevision:
242
            raise errors.FileTimestampUnavailable(path)
243
        return rev.timestamp
244
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
245
    def id2path(self, file_id):
0.200.1712 by Jelmer Vernooij
Add file_id prefix.
246
        try:
0.329.1 by Jelmer Vernooij
Check that path actually exists in Tree.id2path.
247
            path = self._fileid_map.lookup_path(file_id)
0.200.1712 by Jelmer Vernooij
Add file_id prefix.
248
        except ValueError:
0.200.1714 by Jelmer Vernooij
Fix NoSuchId raising.
249
            raise errors.NoSuchId(self, file_id)
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
250
        path = path.decode('utf-8')
0.372.1 by Jelmer Vernooij
Fix some missing id handling.
251
        if self.is_versioned(path):
0.329.1 by Jelmer Vernooij
Check that path actually exists in Tree.id2path.
252
            return path
253
        raise errors.NoSuchId(self, file_id)
254
255
    def is_versioned(self, path):
256
        return self.has_filename(path)
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
257
258
    def path2id(self, path):
0.200.1328 by Jelmer Vernooij
More test fixes.
259
        if self.mapping.is_special_file(path):
260
            return None
261
        return self._fileid_map.lookup_file_id(path.encode('utf-8'))
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
262
0.200.1569 by Jelmer Vernooij
Implement GitRevisionTree.all_file_ids().
263
    def all_file_ids(self):
264
        return set(self._fileid_map.all_file_ids())
265
0.200.1724 by Jelmer Vernooij
Add GitRevisionTree.all_versioned_paths implementation.
266
    def all_versioned_paths(self):
267
        ret = set()
268
        todo = set([('', self.tree)])
269
        while todo:
270
            (path, tree_id) = todo.pop()
0.287.2 by Jelmer Vernooij
Support empty trees in GitRevisionTree.
271
            if tree_id is None:
272
                continue
0.200.1724 by Jelmer Vernooij
Add GitRevisionTree.all_versioned_paths implementation.
273
            tree = self.store[tree_id]
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
274
            for name, mode, hexsha in tree.items():
0.200.1724 by Jelmer Vernooij
Add GitRevisionTree.all_versioned_paths implementation.
275
                subpath = posixpath.join(path, name)
276
                if stat.S_ISDIR(mode):
277
                    todo.add((subpath, hexsha))
278
                else:
279
                    ret.add(subpath)
280
        return ret
281
0.200.1204 by Jelmer Vernooij
Implement GitRevisionTree.get_root_id().
282
    def get_root_id(self):
0.378.1 by Jelmer Vernooij
Return None from Tree.get_root_id() when encountering an empty tree.
283
        if self.tree is None:
284
            return None
0.200.1204 by Jelmer Vernooij
Implement GitRevisionTree.get_root_id().
285
        return self.path2id("")
286
0.200.1208 by Jelmer Vernooij
Add GitWorkingTree.has_id and GitWorkingTree.has_or_had_id.
287
    def has_or_had_id(self, file_id):
0.372.1 by Jelmer Vernooij
Fix some missing id handling.
288
        try:
289
            path = self.id2path(file_id)
290
        except errors.NoSuchId:
291
            return False
292
        return True
0.200.1208 by Jelmer Vernooij
Add GitWorkingTree.has_id and GitWorkingTree.has_or_had_id.
293
294
    def has_id(self, file_id):
295
        try:
296
            path = self.id2path(file_id)
297
        except errors.NoSuchId:
298
            return False
299
        return self.has_filename(path)
300
0.381.1 by Jelmer Vernooij
Standardize looking up entries in Git trees.
301
    def _lookup_path(self, path):
302
        if self.tree is None:
303
            raise errors.NoSuchFile(path)
304
        try:
305
            return tree_lookup_path(self.store.__getitem__, self.tree,
306
                path.encode('utf-8'))
307
        except KeyError:
308
            raise errors.NoSuchFile(self, path)
309
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
310
    def is_executable(self, path, file_id=None):
0.381.1 by Jelmer Vernooij
Standardize looking up entries in Git trees.
311
        (mode, hexsha) = self._lookup_path(path)
0.200.1615 by Jelmer Vernooij
Implement GitRevisionTree.is_executable.:
312
        if mode is None:
313
            # the tree root is a directory
314
            return False
315
        return mode_is_executable(mode)
316
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
317
    def kind(self, path, file_id=None):
0.381.1 by Jelmer Vernooij
Standardize looking up entries in Git trees.
318
        (mode, hexsha) = self._lookup_path(path)
0.200.1253 by Jelmer Vernooij
Fix Tree.kind(TREE_ROOT).
319
        if mode is None:
320
            # the tree root is a directory
321
            return "directory"
0.200.1241 by Jelmer Vernooij
Implement GitRevisionTree.kind.
322
        return mode_kind(mode)
323
0.200.1197 by Jelmer Vernooij
Implement GitRevisionTree.has_filename.
324
    def has_filename(self, path):
325
        try:
0.381.1 by Jelmer Vernooij
Standardize looking up entries in Git trees.
326
            self._lookup_path(path)
327
        except errors.NoSuchFile:
0.200.1197 by Jelmer Vernooij
Implement GitRevisionTree.has_filename.
328
            return False
329
        else:
330
            return True
331
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
332
    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.
333
        if self.tree is None:
334
            return
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
335
        if from_dir is None:
0.200.1197 by Jelmer Vernooij
Implement GitRevisionTree.has_filename.
336
            from_dir = u""
0.381.1 by Jelmer Vernooij
Standardize looking up entries in Git trees.
337
        (mode, hexsha) = self._lookup_path(from_dir)
0.264.9 by Jelmer Vernooij
Implement basic GitWorkingTree.iter_entries_by_dir.
338
        if mode is None: # Root
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
339
            root_ie = self._get_dir_ie(b"", None)
0.264.9 by Jelmer Vernooij
Implement basic GitWorkingTree.iter_entries_by_dir.
340
        else:
0.264.10 by Jelmer Vernooij
Yield inventory entries.
341
            parent_path = posixpath.dirname(from_dir.encode("utf-8"))
0.200.1328 by Jelmer Vernooij
More test fixes.
342
            parent_id = self._fileid_map.lookup_file_id(parent_path)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
343
            if mode_kind(mode) == 'directory':
344
                root_ie = self._get_dir_ie(from_dir.encode("utf-8"), parent_id)
345
            else:
346
                root_ie = self._get_file_ie(from_dir.encode("utf-8"),
347
                    posixpath.basename(from_dir), mode, hexsha)
348
        if from_dir != "" or include_root:
349
            yield (from_dir, "V", root_ie.kind, root_ie.file_id, root_ie)
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
350
        todo = set()
0.264.10 by Jelmer Vernooij
Yield inventory entries.
351
        if root_ie.kind == 'directory':
352
            todo.add((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.
353
        while todo:
0.264.10 by Jelmer Vernooij
Yield inventory entries.
354
            (path, hexsha, parent_id) = todo.pop()
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
355
            tree = self.store[hexsha]
356
            for name, mode, hexsha in tree.iteritems():
0.200.1328 by Jelmer Vernooij
More test fixes.
357
                if self.mapping.is_special_file(name):
358
                    continue
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
359
                child_path = posixpath.join(path, name)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
360
                if stat.S_ISDIR(mode):
361
                    ie = self._get_dir_ie(child_path, parent_id)
362
                    if recursive:
363
                        todo.add((child_path, hexsha, ie.file_id))
364
                else:
365
                    ie = self._get_file_ie(child_path, name, mode, hexsha, parent_id)
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
366
                yield child_path.decode('utf-8'), "V", ie.kind, ie.file_id, ie
0.264.10 by Jelmer Vernooij
Yield inventory entries.
367
368
    def _get_file_ie(self, path, name, mode, hexsha, parent_id):
0.361.1 by Jelmer Vernooij
Don't use assert.
369
        if type(path) is not bytes:
370
            raise TypeError(path)
371
        if type(name) is not bytes:
372
            raise TypeError(name)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
373
        kind = mode_kind(mode)
0.200.1328 by Jelmer Vernooij
More test fixes.
374
        file_id = self._fileid_map.lookup_file_id(path)
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
375
        ie = entry_factory[kind](file_id, name.decode("utf-8"), parent_id)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
376
        if kind == 'symlink':
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
377
            ie.symlink_target = self.store[hexsha].data.decode('utf-8')
0.200.1401 by Jelmer Vernooij
Cope with submodules in working trees.
378
        elif kind == 'tree-reference':
379
            ie.reference_revision = self.mapping.revision_id_foreign_to_bzr(hexsha)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
380
        else:
381
            data = self.store[hexsha].data
382
            ie.text_sha1 = osutils.sha_string(data)
383
            ie.text_size = len(data)
384
            ie.executable = mode_is_executable(mode)
385
        return ie
386
387
    def _get_dir_ie(self, path, parent_id):
0.200.1328 by Jelmer Vernooij
More test fixes.
388
        file_id = self._fileid_map.lookup_file_id(path)
0.390.1 by Jelmer Vernooij
Don't set .revision for returned TreeEntry objects; it's slow, and callers can call Tree.get_file_revision if they really care.
389
        return GitTreeDirectory(file_id,
0.264.10 by Jelmer Vernooij
Yield inventory entries.
390
            posixpath.basename(path).decode("utf-8"), parent_id)
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
391
0.374.1 by Jelmer Vernooij
Implement GitRevisionTree.iter_child_entries.
392
    def iter_child_entries(self, path, file_id=None):
0.381.1 by Jelmer Vernooij
Standardize looking up entries in Git trees.
393
        (mode, tree_sha) = self._lookup_path(path)
394
395
        if not stat.S_ISDIR(mode):
396
            return
397
0.374.1 by Jelmer Vernooij
Implement GitRevisionTree.iter_child_entries.
398
        encoded_path = path.encode('utf-8')
399
        file_id = self.path2id(path)
400
        tree = self.store[tree_sha]
401
        for name, mode, hexsha in tree.iteritems():
402
            if self.mapping.is_special_file(name):
403
                continue
404
            child_path = posixpath.join(encoded_path, name)
405
            if stat.S_ISDIR(mode):
406
                yield self._get_dir_ie(child_path, file_id)
407
            else:
408
                yield self._get_file_ie(child_path, name, mode, hexsha,
409
                                        file_id)
410
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
411
    def iter_entries_by_dir(self, specific_files=None, yield_parents=False):
0.287.4 by Jelmer Vernooij
Fix GitRevisionTree behaviour for null:
412
        if self.tree is None:
413
            return
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
414
        if yield_parents:
415
            # TODO(jelmer): Support yield parents
416
            raise NotImplementedError
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
417
        if specific_files is not None:
418
            if specific_files in ([""], []):
419
                specific_files = None
0.200.1285 by Jelmer Vernooij
Support specific_file_ids argument to Tree.iter_entries_by_dir.
420
            else:
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
421
                specific_files = set([p.encode('utf-8') for p in specific_files])
0.264.10 by Jelmer Vernooij
Yield inventory entries.
422
        todo = set([("", self.tree, None)])
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
423
        while todo:
0.264.10 by Jelmer Vernooij
Yield inventory entries.
424
            path, tree_sha, parent_id = todo.pop()
425
            ie = self._get_dir_ie(path, parent_id)
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
426
            if specific_files is None or path in specific_files:
0.200.1715 by Jelmer Vernooij
Fix some more tests.
427
                yield path.decode("utf-8"), ie
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
428
            tree = self.store[tree_sha]
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
429
            for name, mode, hexsha in tree.iteritems():
0.200.1328 by Jelmer Vernooij
More test fixes.
430
                if self.mapping.is_special_file(name):
431
                    continue
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
432
                child_path = posixpath.join(path, name)
433
                if stat.S_ISDIR(mode):
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
434
                    if (specific_files is None or
435
                        any(filter(lambda p: p.startswith(child_path), specific_files))):
0.200.1285 by Jelmer Vernooij
Support specific_file_ids argument to Tree.iter_entries_by_dir.
436
                        todo.add((child_path, hexsha, ie.file_id))
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
437
                elif specific_files is None or child_path in specific_files:
0.200.1715 by Jelmer Vernooij
Fix some more tests.
438
                    yield (child_path.decode("utf-8"),
0.200.1307 by Jelmer Vernooij
Formatting fixes, specify path to a couple more functions.
439
                            self._get_file_ie(child_path, name, mode, hexsha,
0.200.1285 by Jelmer Vernooij
Support specific_file_ids argument to Tree.iter_entries_by_dir.
440
                           ie.file_id))
0.264.6 by Jelmer Vernooij
Implement custom GitRevisionTree.iter_entries_by_dir, GitRevisionTree.list_files.
441
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
442
    def get_revision_id(self):
0.200.959 by Jelmer Vernooij
Improve docstrings.
443
        """See RevisionTree.get_revision_id."""
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
444
        return self._revision_id
445
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
446
    def get_file_sha1(self, path, file_id=None, stat_value=None):
0.287.6 by Jelmer Vernooij
Fix some more tests.
447
        if self.tree is None:
448
            raise errors.NoSuchFile(path)
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
449
        return osutils.sha_string(self.get_file_text(path, file_id))
0.200.1255 by Jelmer Vernooij
Implement GitRevisionTree.get_file_sha1.
450
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
451
    def get_file_verifier(self, path, file_id=None, stat_value=None):
0.381.1 by Jelmer Vernooij
Standardize looking up entries in Git trees.
452
        (mode, hexsha) = self._lookup_path(path)
0.200.1302 by Jelmer Vernooij
Significantly improve performance of WorkingTree.extras().
453
        return ("GIT", hexsha)
454
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
455
    def get_file_text(self, path, file_id=None):
0.200.959 by Jelmer Vernooij
Improve docstrings.
456
        """See RevisionTree.get_file_text."""
0.381.1 by Jelmer Vernooij
Standardize looking up entries in Git trees.
457
        (mode, hexsha) = self._lookup_path(path)
0.264.3 by Jelmer Vernooij
Make RevisionTree inventoryless.
458
        if stat.S_ISREG(mode):
459
            return self.store[hexsha].data
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
460
        else:
0.335.1 by Jelmer Vernooij
Be a bit stricter about encodings.
461
            return b""
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
462
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
463
    def get_symlink_target(self, path, file_id=None):
0.200.1466 by Jelmer Vernooij
Implement GitRevisionTree.get_symlink_target.
464
        """See RevisionTree.get_symlink_target."""
0.381.1 by Jelmer Vernooij
Standardize looking up entries in Git trees.
465
        (mode, hexsha) = self._lookup_path(path)
0.200.1466 by Jelmer Vernooij
Implement GitRevisionTree.get_symlink_target.
466
        if stat.S_ISLNK(mode):
0.200.1701 by Jelmer Vernooij
Fix a few tests.
467
            return self.store[hexsha].data.decode('utf-8')
0.200.1466 by Jelmer Vernooij
Implement GitRevisionTree.get_symlink_target.
468
        else:
469
            return None
470
0.264.10 by Jelmer Vernooij
Yield inventory entries.
471
    def _comparison_data(self, entry, path):
472
        if entry is None:
473
            return None, False, None
474
        return entry.kind, entry.executable, None
475
0.200.1568 by Jelmer Vernooij
Implement GitRevisionTree.path_content_summary.
476
    def path_content_summary(self, path):
477
        """See Tree.path_content_summary."""
478
        try:
0.381.1 by Jelmer Vernooij
Standardize looking up entries in Git trees.
479
            (mode, hexsha) = self._lookup_path(path)
480
        except errors.NoSuchFile:
0.200.1568 by Jelmer Vernooij
Implement GitRevisionTree.path_content_summary.
481
            return ('missing', None, None, None)
482
        kind = mode_kind(mode)
483
        if kind == 'file':
484
            executable = mode_is_executable(mode)
485
            contents = self.store[hexsha].data
486
            return (kind, len(contents), executable, osutils.sha_string(contents))
487
        elif kind == 'symlink':
488
            return (kind, None, None, self.store[hexsha].data)
489
        else:
490
            return (kind, None, None, None)
491
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
492
    def find_related_paths_across_trees(self, paths, trees=[],
493
            require_versioned=True):
494
        if paths is None:
495
            return None
496
        if require_versioned:
497
            trees = [self] + (trees if trees is not None else [])
498
            unversioned = set()
499
            for p in paths:
500
                for t in trees:
501
                    if t.is_versioned(p):
502
                        break
503
                else:
504
                    unversioned.add(p)
505
            if unversioned:
506
                raise errors.PathsNotVersionedError(unversioned)
507
        return filter(self.is_versioned, paths)
508
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
509
0.252.43 by Jelmer Vernooij
Some refactoring, support proper file ids in revision deltas.
510
def tree_delta_from_git_changes(changes, mapping,
0.200.1743 by Jelmer Vernooij
Fix some revision delta filtering.
511
        (old_fileid_map, new_fileid_map), specific_files=None,
0.287.5 by Jelmer Vernooij
Fix root handling.
512
        require_versioned=False, include_root=False):
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
513
    """Create a TreeDelta from two git trees.
0.200.959 by Jelmer Vernooij
Improve docstrings.
514
515
    source and target are iterators over tuples with:
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
516
        (filename, sha, mode)
517
    """
518
    ret = delta.TreeDelta()
519
    for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes:
0.287.5 by Jelmer Vernooij
Fix root handling.
520
        if newpath == u'' and not include_root:
521
            continue
0.200.1743 by Jelmer Vernooij
Fix some revision delta filtering.
522
        if not (specific_files is None or
0.287.5 by Jelmer Vernooij
Fix root handling.
523
                (oldpath is not None and osutils.is_inside_or_parent_of_any(specific_files, oldpath)) or
524
                (newpath is not None and osutils.is_inside_or_parent_of_any(specific_files, newpath))):
0.200.1743 by Jelmer Vernooij
Fix some revision delta filtering.
525
            continue
0.200.1752 by Jelmer Vernooij
Don't traverse nested trees in WorkingTree.smart_add.
526
        if mapping.is_special_file(oldpath):
0.252.43 by Jelmer Vernooij
Some refactoring, support proper file ids in revision deltas.
527
            oldpath = None
0.200.1752 by Jelmer Vernooij
Don't traverse nested trees in WorkingTree.smart_add.
528
        if mapping.is_special_file(newpath):
0.252.43 by Jelmer Vernooij
Some refactoring, support proper file ids in revision deltas.
529
            newpath = None
530
        if oldpath is None and newpath is None:
531
            continue
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
532
        if oldpath is None:
0.200.1613 by Jelmer Vernooij
Handle encoding better in working tree iter changes.
533
            file_id = new_fileid_map.lookup_file_id(newpath)
534
            ret.added.append((newpath.decode('utf-8'), file_id, mode_kind(newmode)))
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
535
        elif newpath is None:
0.200.1613 by Jelmer Vernooij
Handle encoding better in working tree iter changes.
536
            file_id = old_fileid_map.lookup_file_id(oldpath)
537
            ret.removed.append((oldpath.decode('utf-8'), file_id, mode_kind(oldmode)))
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
538
        elif oldpath != newpath:
0.200.1613 by Jelmer Vernooij
Handle encoding better in working tree iter changes.
539
            file_id = old_fileid_map.lookup_file_id(oldpath)
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
540
            ret.renamed.append(
541
                (oldpath.decode('utf-8'), newpath.decode('utf-8'), file_id,
542
                mode_kind(newmode), (oldsha != newsha),
543
                (oldmode != newmode)))
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
544
        elif mode_kind(oldmode) != mode_kind(newmode):
0.200.1613 by Jelmer Vernooij
Handle encoding better in working tree iter changes.
545
            file_id = new_fileid_map.lookup_file_id(newpath)
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
546
            ret.kind_changed.append(
547
                (newpath.decode('utf-8'), file_id, mode_kind(oldmode),
548
                mode_kind(newmode)))
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
549
        elif oldsha != newsha or oldmode != newmode:
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
550
            if stat.S_ISDIR(oldmode) and stat.S_ISDIR(newmode):
551
                continue
0.200.1613 by Jelmer Vernooij
Handle encoding better in working tree iter changes.
552
            file_id = new_fileid_map.lookup_file_id(newpath)
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
553
            ret.modified.append(
554
                (newpath.decode('utf-8'), file_id, mode_kind(newmode),
555
                (oldsha != newsha), (oldmode != newmode)))
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
556
        else:
0.200.1613 by Jelmer Vernooij
Handle encoding better in working tree iter changes.
557
            file_id = new_fileid_map.lookup_file_id(newpath)
558
            ret.unchanged.append((newpath.decode('utf-8'), file_id, mode_kind(newmode)))
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
559
    return ret
560
561
0.391.2 by Jelmer Vernooij
Pass along target_missing.
562
def changes_from_git_changes(changes, mapping, specific_files=None, include_unchanged=False,
563
                             target_missing=None):
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
564
    """Create a iter_changes-like generator from a git stream.
0.200.959 by Jelmer Vernooij
Improve docstrings.
565
566
    source and target are iterators over tuples with:
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
567
        (filename, sha, mode)
568
    """
0.391.2 by Jelmer Vernooij
Pass along target_missing.
569
    if target_missing is None:
570
        target_missing = set()
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
571
    for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes:
0.200.1743 by Jelmer Vernooij
Fix some revision delta filtering.
572
        if not (specific_files is None or
0.287.5 by Jelmer Vernooij
Fix root handling.
573
                (oldpath is not None and osutils.is_inside_or_parent_of_any(specific_files, oldpath)) or
574
                (newpath is not None and osutils.is_inside_or_parent_of_any(specific_files, newpath))):
0.200.1743 by Jelmer Vernooij
Fix some revision delta filtering.
575
            continue
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
576
        path = (oldpath, newpath)
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
577
        if oldpath is not None and mapping.is_special_file(oldpath):
578
            continue
579
        if newpath is not None and mapping.is_special_file(newpath):
0.200.1328 by Jelmer Vernooij
More test fixes.
580
            continue
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
581
        if oldpath is None:
582
            fileid = mapping.generate_file_id(newpath)
583
            oldexe = None
584
            oldkind = None
585
            oldname = None
586
            oldparent = None
587
        else:
0.200.1345 by Jelmer Vernooij
paths should be unicode when yielded by iter_changes.
588
            oldpath = oldpath.decode("utf-8")
0.361.1 by Jelmer Vernooij
Don't use assert.
589
            if oldmode is None:
590
                raise ValueError
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
591
            oldexe = mode_is_executable(oldmode)
592
            oldkind = mode_kind(oldmode)
0.316.1 by Jelmer Vernooij
Fix iter_changes behaviour for trees in the tree root.
593
            if oldpath == u'':
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
594
                oldparent = None
0.316.1 by Jelmer Vernooij
Fix iter_changes behaviour for trees in the tree root.
595
                oldname = ''
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
596
            else:
0.316.1 by Jelmer Vernooij
Fix iter_changes behaviour for trees in the tree root.
597
                (oldparentpath, oldname) = osutils.split(oldpath)
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
598
                oldparent = mapping.generate_file_id(oldparentpath)
599
            fileid = mapping.generate_file_id(oldpath)
600
        if newpath is None:
601
            newexe = None
602
            newkind = None
603
            newname = None
604
            newparent = None
605
        else:
0.200.1345 by Jelmer Vernooij
paths should be unicode when yielded by iter_changes.
606
            newpath = newpath.decode("utf-8")
0.200.1576 by Jelmer Vernooij
Merge a bunch of fixes from store-roundtrip-info.
607
            if newmode is not None:
608
                newexe = mode_is_executable(newmode)
609
                newkind = mode_kind(newmode)
610
            else:
611
                newexe = False
612
                newkind = None
0.316.1 by Jelmer Vernooij
Fix iter_changes behaviour for trees in the tree root.
613
            if newpath == u'':
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
614
                newparent = None
0.316.1 by Jelmer Vernooij
Fix iter_changes behaviour for trees in the tree root.
615
                newname = u''
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
616
            else:
0.316.1 by Jelmer Vernooij
Fix iter_changes behaviour for trees in the tree root.
617
                newparentpath, newname = osutils.split(newpath)
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
618
                newparent = mapping.generate_file_id(newparentpath)
0.357.3 by Jelmer Vernooij
More remove fixes.
619
        if (not include_unchanged and
620
            oldkind == 'directory' and newkind == 'directory' and
621
            oldpath == newpath):
622
            continue
0.200.959 by Jelmer Vernooij
Improve docstrings.
623
        yield (fileid, (oldpath, newpath), (oldsha != newsha),
0.391.2 by Jelmer Vernooij
Pass along target_missing.
624
             (oldpath is not None, newpath is not None or newpath in target_missing),
0.200.959 by Jelmer Vernooij
Improve docstrings.
625
             (oldparent, newparent), (oldname, newname),
626
             (oldkind, newkind), (oldexe, newexe))
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
627
628
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
629
class InterGitTrees(_mod_tree.InterTree):
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
630
    """InterTree that works between two git trees."""
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
631
0.200.659 by Jelmer Vernooij
Prevent tests using InterGitRevisionTrees.
632
    _matching_from_tree_format = None
633
    _matching_to_tree_format = None
634
    _test_mutable_trees_to_test_trees = None
635
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
636
    @classmethod
637
    def is_compatible(cls, source, target):
0.200.959 by Jelmer Vernooij
Improve docstrings.
638
        return (isinstance(source, GitRevisionTree) and
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
639
                isinstance(target, GitRevisionTree))
640
641
    def compare(self, want_unchanged=False, specific_files=None,
642
                extra_trees=None, require_versioned=False, include_root=False,
643
                want_unversioned=False):
0.391.2 by Jelmer Vernooij
Pass along target_missing.
644
        changes, target_missing = self._iter_git_changes(
645
                want_unchanged=want_unchanged,
0.357.1 by Jelmer Vernooij
Fix some remove tests.
646
                require_versioned=require_versioned,
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
647
                specific_files=specific_files,
648
                extra_trees=extra_trees)
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
649
        source_fileid_map = self.source._fileid_map
650
        target_fileid_map = self.target._fileid_map
0.200.959 by Jelmer Vernooij
Improve docstrings.
651
        return tree_delta_from_git_changes(changes, self.target.mapping,
0.252.43 by Jelmer Vernooij
Some refactoring, support proper file ids in revision deltas.
652
            (source_fileid_map, target_fileid_map),
0.287.5 by Jelmer Vernooij
Fix root handling.
653
            specific_files=specific_files, include_root=include_root)
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
654
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
655
    def iter_changes(self, include_unchanged=False, specific_files=None,
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
656
                     pb=None, extra_trees=[], require_versioned=True,
657
                     want_unversioned=False):
0.391.2 by Jelmer Vernooij
Pass along target_missing.
658
        changes, target_missing = self._iter_git_changes(
659
                want_unchanged=include_unchanged,
0.357.1 by Jelmer Vernooij
Fix some remove tests.
660
                require_versioned=require_versioned,
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
661
                specific_files=specific_files,
662
                extra_trees=extra_trees)
0.200.959 by Jelmer Vernooij
Improve docstrings.
663
        return changes_from_git_changes(changes, self.target.mapping,
0.391.2 by Jelmer Vernooij
Pass along target_missing.
664
            specific_files=specific_files, include_unchanged=include_unchanged,
665
            target_missing=target_missing)
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
666
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
667
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
668
            require_versioned=False, extra_trees=None):
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
669
        raise NotImplementedError(self._iter_git_changes)
670
671
672
class InterGitRevisionTrees(InterGitTrees):
673
    """InterTree that works between two git revision trees."""
674
675
    _matching_from_tree_format = None
676
    _matching_to_tree_format = None
677
    _test_mutable_trees_to_test_trees = None
678
679
    @classmethod
680
    def is_compatible(cls, source, target):
681
        return (isinstance(source, GitRevisionTree) and
682
                isinstance(target, GitRevisionTree))
683
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
684
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
685
            require_versioned=True, extra_trees=None):
686
        trees = [self.source]
687
        if extra_trees is not None:
688
            trees.extend(extra_trees)
689
        if specific_files is not None:
690
            specific_files = self.target.find_related_paths_across_trees(
691
                    specific_files, trees,
692
                    require_versioned=require_versioned)
0.357.1 by Jelmer Vernooij
Fix some remove tests.
693
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
694
        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.
695
            store = OverlayObjectStore([self.source._repository._git.object_store,
696
                                        self.target._repository._git.object_store])
697
        else:
698
            store = self.source._repository._git.object_store
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
699
        return self.source._repository._git.object_store.tree_changes(
700
            self.source.tree, self.target.tree, want_unchanged=want_unchanged,
0.391.2 by Jelmer Vernooij
Pass along target_missing.
701
            include_trees=True, change_type_same=True), set()
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
702
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
703
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
704
_mod_tree.InterTree.register_optimiser(InterGitRevisionTrees)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
705
706
707
class MutableGitIndexTree(mutabletree.MutableTree):
708
709
    def __init__(self):
710
        self._lock_mode = None
711
        self._lock_count = 0
712
        self._versioned_dirs = None
713
714
    def is_versioned(self, path):
715
        with self.lock_read():
716
            path = path.rstrip('/').encode('utf-8')
717
            return (path in self.index or self._has_dir(path))
718
719
    def _has_dir(self, path):
720
        if path == "":
721
            return True
722
        if self._versioned_dirs is None:
723
            self._load_dirs()
724
        return path in self._versioned_dirs
725
726
    def _load_dirs(self):
0.361.3 by Jelmer Vernooij
Merge trunk,
727
        if self._lock_mode is None:
728
            raise errors.ObjectNotLocked(self)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
729
        self._versioned_dirs = set()
730
        for p in self.index:
731
            self._ensure_versioned_dir(posixpath.dirname(p))
732
733
    def _ensure_versioned_dir(self, dirname):
734
        if dirname in self._versioned_dirs:
735
            return
736
        if dirname != "":
737
            self._ensure_versioned_dir(posixpath.dirname(dirname))
738
        self._versioned_dirs.add(dirname)
739
740
    def path2id(self, path):
741
        with self.lock_read():
742
            path = path.rstrip('/')
743
            if self.is_versioned(path.rstrip('/')):
744
                return self._fileid_map.lookup_file_id(path.encode("utf-8"))
745
            return None
746
747
    def has_id(self, file_id):
748
        try:
749
            self.id2path(file_id)
750
        except errors.NoSuchId:
751
            return False
752
        else:
753
            return True
754
755
    def id2path(self, file_id):
756
        if file_id is None:
757
            return ''
0.361.3 by Jelmer Vernooij
Merge trunk,
758
        if type(file_id) is not bytes:
759
            raise TypeError(file_id)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
760
        with self.lock_read():
761
            try:
762
                path = self._fileid_map.lookup_path(file_id)
763
            except ValueError:
764
                raise errors.NoSuchId(self, file_id)
765
            path = path.decode('utf-8')
766
            if self.is_versioned(path):
767
                return path
768
            raise errors.NoSuchId(self, file_id)
769
770
    def _set_root_id(self, file_id):
771
        self._fileid_map.set_file_id("", file_id)
772
773
    def get_root_id(self):
774
        return self.path2id("")
775
776
    def _add(self, files, ids, kinds):
777
        for (path, file_id, kind) in zip(files, ids, kinds):
778
            if file_id is not None:
779
                raise workingtree.SettingFileIdUnsupported()
0.366.1 by Jelmer Vernooij
Fix normalized filename checking in add.
780
            path, can_access = osutils.normalized_filename(path)
781
            if not can_access:
782
                raise errors.InvalidNormalization(path)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
783
            self._index_add_entry(path, kind)
784
785
    def _index_add_entry(self, path, kind, flags=0):
0.361.4 by Jelmer Vernooij
Fix assert.
786
        if not isinstance(path, basestring):
0.361.3 by Jelmer Vernooij
Merge trunk,
787
            raise TypeError(path)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
788
        if kind == "directory":
789
            # Git indexes don't contain directories
790
            return
791
        if kind == "file":
792
            blob = Blob()
793
            try:
794
                file, stat_val = self.get_file_with_stat(path)
795
            except (errors.NoSuchFile, IOError):
796
                # TODO: Rather than come up with something here, use the old index
797
                file = BytesIO()
798
                stat_val = os.stat_result(
799
                    (stat.S_IFREG | 0644, 0, 0, 0, 0, 0, 0, 0, 0, 0))
800
            blob.set_raw_string(file.read())
801
        elif kind == "symlink":
802
            blob = Blob()
803
            try:
804
                stat_val = self._lstat(path)
805
            except (errors.NoSuchFile, OSError):
806
                # TODO: Rather than come up with something here, use the
807
                # old index
808
                stat_val = os.stat_result(
809
                    (stat.S_IFLNK, 0, 0, 0, 0, 0, 0, 0, 0, 0))
810
            blob.set_raw_string(
811
                self.get_symlink_target(path).encode("utf-8"))
812
        else:
813
            raise AssertionError("unknown kind '%s'" % kind)
814
        # Add object to the repository if it didn't exist yet
815
        if not blob.id in self.store:
816
            self.store.add_object(blob)
817
        # Add an entry to the index or update the existing entry
818
        ensure_normalized_path(path)
819
        encoded_path = path.encode("utf-8")
820
        if b'\r' in encoded_path or b'\n' in encoded_path:
821
            # TODO(jelmer): Why do we need to do this?
822
            trace.mutter('ignoring path with invalid newline in it: %r', path)
823
            return
824
        self.index[encoded_path] = index_entry_from_stat(
825
            stat_val, blob.id, flags)
826
        if self._versioned_dirs is not None:
827
            self._ensure_versioned_dir(encoded_path)
828
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
829
    def iter_entries_by_dir(self, specific_files=None, yield_parents=False):
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
830
        if yield_parents:
831
            raise NotImplementedError(self.iter_entries_by_dir)
832
        with self.lock_read():
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
833
            if specific_files is not None:
834
                specific_files = set(specific_files)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
835
            else:
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
836
                specific_files = None
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
837
            root_ie = self._get_dir_ie(u"", None)
838
            ret = {}
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
839
            if specific_files is None or u"" in specific_files:
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
840
                ret[(None, u"")] = root_ie
841
            dir_ids = {u"": root_ie.file_id}
842
            for path, value in self.index.iteritems():
843
                if self.mapping.is_special_file(path):
844
                    continue
845
                path = path.decode("utf-8")
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
846
                if specific_files is not None and not path in specific_files:
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
847
                    continue
848
                (parent, name) = posixpath.split(path)
849
                try:
850
                    file_ie = self._get_file_ie(name, path, value, None)
851
                except errors.NoSuchFile:
852
                    continue
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
853
                if yield_parents or specific_files is None:
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
854
                    for (dir_path, dir_ie) in self._add_missing_parent_ids(parent,
855
                            dir_ids):
856
                        ret[(posixpath.dirname(dir_path), dir_path)] = dir_ie
857
                file_ie.parent_id = self.path2id(parent)
858
                ret[(posixpath.dirname(path), path)] = file_ie
859
            return ((path, ie) for ((_, path), ie) in sorted(ret.items()))
860
861
862
    def _get_dir_ie(self, path, parent_id):
863
        file_id = self.path2id(path)
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
864
        return GitTreeDirectory(file_id,
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
865
            posixpath.basename(path).strip("/"), parent_id)
866
867
    def _get_file_ie(self, name, path, value, parent_id):
0.361.3 by Jelmer Vernooij
Merge trunk,
868
        if type(name) is not unicode:
869
            raise TypeError(name)
870
        if type(path) is not unicode:
871
            raise TypeError(path)
872
        if not isinstance(value, tuple) or len(value) != 10:
873
            raise TypeError(value)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
874
        (ctime, mtime, dev, ino, mode, uid, gid, size, sha, flags) = value
875
        file_id = self.path2id(path)
876
        if type(file_id) != str:
877
            raise AssertionError
878
        kind = mode_kind(mode)
0.365.1 by Jelmer Vernooij
Add custom GitTree{Directory,File,Symlink}.
879
        ie = entry_factory[kind](file_id, name, parent_id)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
880
        if kind == 'symlink':
881
            ie.symlink_target = self.get_symlink_target(path, file_id)
882
        else:
883
            try:
884
                data = self.get_file_text(path, file_id)
885
            except errors.NoSuchFile:
886
                data = None
887
            except IOError as e:
888
                if e.errno != errno.ENOENT:
889
                    raise
890
                data = None
891
            if data is None:
892
                data = self.branch.repository._git.object_store[sha].data
893
            ie.text_sha1 = osutils.sha_string(data)
894
            ie.text_size = len(data)
895
            ie.executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
896
        return ie
897
898
    def _add_missing_parent_ids(self, path, dir_ids):
899
        if path in dir_ids:
900
            return []
901
        parent = posixpath.dirname(path).strip("/")
902
        ret = self._add_missing_parent_ids(parent, dir_ids)
903
        parent_id = dir_ids[parent]
904
        ie = self._get_dir_ie(path, parent_id)
905
        dir_ids[path] = ie.file_id
906
        ret.append((path, ie))
907
        return ret
908
909
    def _comparison_data(self, entry, path):
910
        if entry is None:
911
            return None, False, None
912
        return entry.kind, entry.executable, None
913
914
    def _unversion_path(self, path):
0.361.3 by Jelmer Vernooij
Merge trunk,
915
        if self._lock_mode is None:
916
            raise errors.ObjectNotLocked(self)
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
917
        encoded_path = path.encode("utf-8")
918
        count = 0
919
        try:
920
            del self.index[encoded_path]
921
        except KeyError:
922
            # A directory, perhaps?
923
            for p in list(self.index):
924
                if p.startswith(encoded_path+b"/"):
925
                    count += 1
926
                    del self.index[p]
927
        else:
928
            count = 1
929
        self._versioned_dirs = None
930
        return count
931
932
    def unversion(self, paths, file_ids=None):
933
        with self.lock_tree_write():
934
            for path in paths:
935
                if self._unversion_path(path) == 0:
936
                    raise errors.NoSuchFile(path)
937
            self._versioned_dirs = None
938
            self.flush()
939
940
    def flush(self):
941
        pass
942
943
    def update_basis_by_delta(self, revid, delta):
944
        # TODO(jelmer): This shouldn't be called, it's inventory specific.
945
        for (old_path, new_path, file_id, ie) in delta:
946
            if old_path is not None and old_path.encode('utf-8') in self.index:
947
                del self.index[old_path.encode('utf-8')]
948
                self._versioned_dirs = None
949
            if new_path is not None and ie.kind != 'directory':
950
                self._index_add_entry(new_path, ie.kind)
951
        self.flush()
952
        self._set_merges_from_parent_ids([])
0.360.4 by Jelmer Vernooij
Implement MemoryTree.rename_one, MemoryTree.mkdir.
953
954
    def move(self, from_paths, to_dir=None, after=None):
955
        rename_tuples = []
956
        with self.lock_tree_write():
957
            to_abs = self.abspath(to_dir)
958
            if not os.path.isdir(to_abs):
959
                raise errors.BzrMoveFailedError('', to_dir,
960
                    errors.NotADirectory(to_abs))
961
962
            for from_rel in from_paths:
963
                from_tail = os.path.split(from_rel)[-1]
964
                to_rel = os.path.join(to_dir, from_tail)
965
                self.rename_one(from_rel, to_rel, after=after)
966
                rename_tuples.append((from_rel, to_rel))
967
            self.flush()
968
            return rename_tuples
969
970
    def rename_one(self, from_rel, to_rel, after=None):
971
        from_path = from_rel.encode("utf-8")
972
        to_rel, can_access = osutils.normalized_filename(to_rel)
973
        if not can_access:
974
            raise errors.InvalidNormalization(to_rel)
975
        to_path = to_rel.encode("utf-8")
976
        with self.lock_tree_write():
977
            if not after:
978
                # Perhaps it's already moved?
979
                after = (
980
                    not self.has_filename(from_rel) and
981
                    self.has_filename(to_rel) and
982
                    not self.is_versioned(to_rel))
983
            if after:
984
                if not self.has_filename(to_rel):
985
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
986
                        errors.NoSuchFile(to_rel))
987
                if self.basis_tree().is_versioned(to_rel):
988
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
989
                        errors.AlreadyVersionedError(to_rel))
990
991
                kind = self.kind(to_rel)
992
            else:
993
                try:
994
                    to_kind = self.kind(to_rel)
995
                except errors.NoSuchFile:
996
                    exc_type = errors.BzrRenameFailedError
997
                    to_kind = None
998
                else:
999
                    exc_type = errors.BzrMoveFailedError
1000
                if self.is_versioned(to_rel):
1001
                    raise exc_type(from_rel, to_rel,
1002
                        errors.AlreadyVersionedError(to_rel))
1003
                if not self.has_filename(from_rel):
1004
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
1005
                        errors.NoSuchFile(from_rel))
0.388.1 by Jelmer Vernooij
Don't print error moving to an unversioned directory.
1006
                kind = self.kind(from_rel)
1007
                if not self.is_versioned(from_rel) and kind != 'directory':
0.360.4 by Jelmer Vernooij
Implement MemoryTree.rename_one, MemoryTree.mkdir.
1008
                    raise exc_type(from_rel, to_rel,
1009
                        errors.NotVersionedError(from_rel))
1010
                if self.has_filename(to_rel):
1011
                    raise errors.RenameFailedFilesExist(
1012
                        from_rel, to_rel, errors.FileExists(to_rel))
1013
1014
                kind = self.kind(from_rel)
1015
1016
            if not after and not from_path in self.index and kind != 'directory':
1017
                # It's not a file
1018
                raise errors.BzrMoveFailedError(from_rel, to_rel,
1019
                    errors.NotVersionedError(path=from_rel))
1020
1021
            if not after:
1022
                try:
1023
                    self._rename_one(from_rel, to_rel)
1024
                except OSError as e:
1025
                    if e.errno == errno.ENOENT:
1026
                        raise errors.BzrMoveFailedError(from_rel, to_rel,
1027
                            errors.NoSuchFile(to_rel))
1028
                    raise
1029
            if kind != 'directory':
1030
                try:
1031
                    del self.index[from_path]
1032
                except KeyError:
1033
                    pass
1034
                self._index_add_entry(to_rel, kind)
1035
            else:
1036
                todo = [p for p in self.index if p.startswith(from_path+'/')]
1037
                for p in todo:
1038
                    self.index[posixpath.join(to_path, posixpath.relpath(p, from_path))] = self.index[p]
1039
                    del self.index[p]
1040
1041
            self._versioned_dirs = None
1042
            self.flush()
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
1043
1044
    def find_related_paths_across_trees(self, paths, trees=[],
1045
            require_versioned=True):
1046
        if paths is None:
1047
            return None
1048
1049
        if require_versioned:
1050
            trees = [self] + (trees if trees is not None else [])
1051
            unversioned = set()
1052
            for p in paths:
1053
                for t in trees:
1054
                    if t.is_versioned(p):
1055
                        break
1056
                else:
1057
                    unversioned.add(p)
1058
            if unversioned:
1059
                raise errors.PathsNotVersionedError(unversioned)
1060
1061
        return filter(self.is_versioned, paths)