/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/git/tree.py

Add a recurse_nested argument to Tree.list_files.

Merged from https://code.launchpad.net/~jelmer/brz/follow-tree-references-list-files/+merge/374066

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
from io import BytesIO
25
25
import os
26
26
 
 
27
from dulwich.config import (
 
28
    parse_submodules,
 
29
    ConfigFile as GitConfigFile,
 
30
    )
 
31
from dulwich.diff_tree import tree_changes
 
32
from dulwich.errors import NotTreeError
27
33
from dulwich.index import (
28
34
    blob_from_path_and_stat,
29
35
    cleanup_mode,
30
36
    commit_tree,
31
37
    index_entry_from_stat,
 
38
    Index,
32
39
    )
33
40
from dulwich.object_store import (
34
41
    tree_lookup_path,
69
76
    mode_kind,
70
77
    default_mapping,
71
78
    )
 
79
from .transportgit import (
 
80
    TransportObjectStore,
 
81
    TransportRepo,
 
82
    )
72
83
 
73
84
 
74
85
class GitTreeDirectory(_mod_tree.TreeDirectory):
189
200
            self.symlink_target)
190
201
 
191
202
 
192
 
class GitTreeSubmodule(_mod_tree.TreeLink):
 
203
class GitTreeSubmodule(_mod_tree.TreeReference):
193
204
 
194
205
    __slots__ = ['file_id', 'name', 'parent_id', 'reference_revision']
195
206
 
286
297
        try:
287
298
            info = self._submodule_info()[relpath]
288
299
        except KeyError:
289
 
            nested_repo_transport = self._repository.user_transport.clone(relpath.decode('utf-8'))
 
300
            nested_repo_transport = self._repository.controldir.user_transport.clone(
 
301
                relpath.decode('utf-8'))
290
302
        else:
291
 
            nested_repo_transport = self._repository.control_transport.clone(
292
 
                posixpath.join('modules', info[0]))
 
303
            nested_repo_transport = self._repository.controldir.control_transport.clone(
 
304
                posixpath.join('modules', info[1]))
293
305
        nested_controldir = _mod_controldir.ControlDir.open_from_transport(
294
306
            nested_repo_transport)
295
307
        return nested_controldir.find_repository()
296
308
 
 
309
    def _get_submodule_store(self, relpath):
 
310
        return self._get_submodule_repository(relpath)._git.object_store
 
311
 
297
312
    def get_nested_tree(self, path):
298
313
        encoded_path = path.encode('utf-8')
299
314
        nested_repo = self._get_submodule_repository(encoded_path)
363
378
    def _lookup_path(self, path):
364
379
        if self.tree is None:
365
380
            raise errors.NoSuchFile(path)
366
 
        try:
367
 
            (mode, hexsha) = tree_lookup_path(
368
 
                self.store.__getitem__, self.tree, path.encode('utf-8'))
369
 
        except KeyError:
370
 
            raise errors.NoSuchFile(self, path)
371
 
        else:
372
 
            return (self.store, mode, hexsha)
 
381
 
 
382
        encoded_path = path.encode('utf-8')
 
383
        parts = encoded_path.split(b'/')
 
384
        hexsha = self.tree
 
385
        store = self.store
 
386
        mode = None
 
387
        for i, p in enumerate(parts):
 
388
            if not p:
 
389
                continue
 
390
            obj = store[hexsha]
 
391
            if not isinstance(obj, Tree):
 
392
                raise NotTreeError(hexsha)
 
393
            try:
 
394
                mode, hexsha = obj[p]
 
395
            except KeyError:
 
396
                raise errors.NoSuchFile(path)
 
397
            if S_ISGITLINK(mode) and i != len(parts) - 1:
 
398
                store = self._get_submodule_store(b'/'.join(parts[:i + 1]))
 
399
                hexsha = store[hexsha].tree
 
400
        return (store, mode, hexsha)
373
401
 
374
402
    def is_executable(self, path):
375
403
        (store, mode, hexsha) = self._lookup_path(path)
393
421
        else:
394
422
            return True
395
423
 
396
 
    def list_files(self, include_root=False, from_dir=None, recursive=True):
 
424
    def _submodule_info(self):
 
425
        if self._submodules is None:
 
426
            try:
 
427
                with self.get_file('.gitmodules') as f:
 
428
                    config = GitConfigFile.from_file(f)
 
429
                    self._submodules = {
 
430
                        path: (url, section)
 
431
                        for path, url, section in parse_submodules(config)}
 
432
            except errors.NoSuchFile:
 
433
                self._submodules = {}
 
434
        return self._submodules
 
435
 
 
436
    def list_files(self, include_root=False, from_dir=None, recursive=True,
 
437
                   recurse_nested=False):
397
438
        if self.tree is None:
398
439
            return
399
440
        if from_dir is None or from_dir == '.':
424
465
                    continue
425
466
                child_path = posixpath.join(path, name)
426
467
                child_relpath = posixpath.join(relpath, name)
 
468
                if S_ISGITLINK(mode) and recurse_nested:
 
469
                    mode = stat.S_IFDIR
 
470
                    store = self._get_submodule_store(child_relpath)
 
471
                    hexsha = store[hexsha].tree
427
472
                if stat.S_ISDIR(mode):
428
473
                    ie = self._get_dir_ie(child_path, parent_id)
429
474
                    if recursive:
973
1018
        self._lock_count = 0
974
1019
        self._versioned_dirs = None
975
1020
        self._index_dirty = False
 
1021
        self._submodules = None
976
1022
 
977
1023
    def is_versioned(self, path):
978
1024
        with self.lock_read():
993
1039
        if self._lock_mode is None:
994
1040
            raise errors.ObjectNotLocked(self)
995
1041
        self._versioned_dirs = set()
996
 
        # TODO(jelmer): Browse over all indexes
997
1042
        for p, i in self._recurse_index_entries():
998
1043
            self._ensure_versioned_dir(posixpath.dirname(p))
999
1044
 
1043
1088
    def _read_submodule_head(self, path):
1044
1089
        raise NotImplementedError(self._read_submodule_head)
1045
1090
 
 
1091
    def _submodule_info(self):
 
1092
        if self._submodules is None:
 
1093
            try:
 
1094
                with self.get_file('.gitmodules') as f:
 
1095
                    config = GitConfigFile.from_file(f)
 
1096
                    self._submodules = {
 
1097
                        path: (url, section)
 
1098
                        for path, url, section in parse_submodules(config)}
 
1099
            except errors.NoSuchFile:
 
1100
                self._submodules = {}
 
1101
        return self._submodules
 
1102
 
1046
1103
    def _lookup_index(self, encoded_path):
1047
1104
        if not isinstance(encoded_path, bytes):
1048
1105
            raise TypeError(encoded_path)
1049
 
        # TODO(jelmer): Look in other indexes
1050
 
        return self.index, encoded_path
 
1106
        # Common case:
 
1107
        if encoded_path in self.index:
 
1108
            return self.index, encoded_path
 
1109
        # TODO(jelmer): Perhaps have a cache with paths under which some
 
1110
        # submodules exist?
 
1111
        index = self.index
 
1112
        remaining_path = encoded_path
 
1113
        while True:
 
1114
            parts = remaining_path.split(b'/')
 
1115
            for i in range(1, len(parts)):
 
1116
                basepath = b'/'.join(parts[:i])
 
1117
                try:
 
1118
                    (ctime, mtime, dev, ino, mode, uid, gid, size, sha,
 
1119
                     flags) = index[basepath]
 
1120
                except KeyError:
 
1121
                    continue
 
1122
                else:
 
1123
                    if S_ISGITLINK(mode):
 
1124
                        index = self._get_submodule_index(basepath)
 
1125
                        remaining_path = b'/'.join(parts[i:])
 
1126
                        break
 
1127
                    else:
 
1128
                        return index, remaining_path
 
1129
            else:
 
1130
                return index, remaining_path
 
1131
        return index, remaining_path
1051
1132
 
1052
1133
    def _index_del_entry(self, index, path):
1053
1134
        del index[path]
1118
1199
        if self._versioned_dirs is not None:
1119
1200
            self._ensure_versioned_dir(index_path)
1120
1201
 
1121
 
    def _recurse_index_entries(self, index=None, basepath=b""):
 
1202
    def _recurse_index_entries(self, index=None, basepath=b"",
 
1203
                               recurse_nested=False):
1122
1204
        # Iterate over all index entries
1123
1205
        with self.lock_read():
1124
1206
            if index is None:
1125
1207
                index = self.index
1126
1208
            for path, value in index.items():
1127
 
                yield (posixpath.join(basepath, path), value)
1128
1209
                (ctime, mtime, dev, ino, mode, uid, gid, size, sha,
1129
1210
                 flags) = value
1130
 
                if S_ISGITLINK(mode):
1131
 
                    pass  # TODO(jelmer): dive into submodule
 
1211
                if S_ISGITLINK(mode) and recurse_nested:
 
1212
                    subindex = self._get_submodule_index(path)
 
1213
                    for entry in self._recurse_index_entries(
 
1214
                            index=subindex, basepath=path,
 
1215
                            recurse_nested=recurse_nested):
 
1216
                        yield entry
 
1217
                else:
 
1218
                    yield (posixpath.join(basepath, path), value)
1132
1219
 
1133
1220
    def iter_entries_by_dir(self, specific_files=None):
1134
1221
        with self.lock_read():
1167
1254
            return ((path, ie) for ((_, path), ie) in sorted(viewitems(ret)))
1168
1255
 
1169
1256
    def iter_references(self):
1170
 
        # TODO(jelmer): Implement a more efficient version of this
1171
 
        for path, entry in self.iter_entries_by_dir():
1172
 
            if entry.kind == 'tree-reference':
1173
 
                yield path
 
1257
        if self.supports_tree_reference():
 
1258
            # TODO(jelmer): Implement a more efficient version of this
 
1259
            for path, entry in self.iter_entries_by_dir():
 
1260
                if entry.kind == 'tree-reference':
 
1261
                    yield path
1174
1262
 
1175
1263
    def _get_dir_ie(self, path, parent_id):
1176
1264
        file_id = self.path2id(path)
1417
1505
        else:
1418
1506
            return (kind, None, None, None)
1419
1507
 
 
1508
    def stored_kind(self, relpath):
 
1509
        (index, index_path) = self._lookup_index(relpath.encode('utf-8'))
 
1510
        if index is None:
 
1511
            return kind
 
1512
        try:
 
1513
            mode = index[index_path].mode
 
1514
        except KeyError:
 
1515
            return kind
 
1516
        else:
 
1517
            if S_ISGITLINK(mode):
 
1518
                return 'tree-reference'
 
1519
            return 'directory'
 
1520
 
1420
1521
    def kind(self, relpath):
1421
1522
        kind = osutils.file_kind(self.abspath(relpath))
1422
1523
        if kind == 'directory':
1423
 
            (index, index_path) = self._lookup_index(relpath.encode('utf-8'))
1424
 
            if index is None:
1425
 
                return kind
1426
 
            try:
1427
 
                mode = index[index_path].mode
1428
 
            except KeyError:
1429
 
                return kind
1430
 
            else:
1431
 
                if S_ISGITLINK(mode):
1432
 
                    return 'tree-reference'
1433
 
                return 'directory'
 
1524
            if self._directory_is_tree_reference(relpath):
 
1525
                return 'tree-reference'
 
1526
            return 'directory'
1434
1527
        else:
1435
1528
            return kind
1436
1529