24
24
from io import BytesIO
27
from dulwich.config import (
29
ConfigFile as GitConfigFile,
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,
31
37
index_entry_from_stat,
33
40
from dulwich.object_store import (
189
200
self.symlink_target)
192
class GitTreeSubmodule(_mod_tree.TreeLink):
203
class GitTreeSubmodule(_mod_tree.TreeReference):
194
205
__slots__ = ['file_id', 'name', 'parent_id', 'reference_revision']
287
298
info = self._submodule_info()[relpath]
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'))
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()
309
def _get_submodule_store(self, relpath):
310
return self._get_submodule_repository(relpath)._git.object_store
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)
367
(mode, hexsha) = tree_lookup_path(
368
self.store.__getitem__, self.tree, path.encode('utf-8'))
370
raise errors.NoSuchFile(self, path)
372
return (self.store, mode, hexsha)
382
encoded_path = path.encode('utf-8')
383
parts = encoded_path.split(b'/')
387
for i, p in enumerate(parts):
391
if not isinstance(obj, Tree):
392
raise NotTreeError(hexsha)
394
mode, hexsha = obj[p]
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)
374
402
def is_executable(self, path):
375
403
(store, mode, hexsha) = self._lookup_path(path)
396
def list_files(self, include_root=False, from_dir=None, recursive=True):
424
def _submodule_info(self):
425
if self._submodules is None:
427
with self.get_file('.gitmodules') as f:
428
config = GitConfigFile.from_file(f)
431
for path, url, section in parse_submodules(config)}
432
except errors.NoSuchFile:
433
self._submodules = {}
434
return self._submodules
436
def list_files(self, include_root=False, from_dir=None, recursive=True,
437
recurse_nested=False):
397
438
if self.tree is None:
399
440
if from_dir is None or from_dir == '.':
425
466
child_path = posixpath.join(path, name)
426
467
child_relpath = posixpath.join(relpath, name)
468
if S_ISGITLINK(mode) and recurse_nested:
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)
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))
1043
1088
def _read_submodule_head(self, path):
1044
1089
raise NotImplementedError(self._read_submodule_head)
1091
def _submodule_info(self):
1092
if self._submodules is None:
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
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
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
1112
remaining_path = encoded_path
1114
parts = remaining_path.split(b'/')
1115
for i in range(1, len(parts)):
1116
basepath = b'/'.join(parts[:i])
1118
(ctime, mtime, dev, ino, mode, uid, gid, size, sha,
1119
flags) = index[basepath]
1123
if S_ISGITLINK(mode):
1124
index = self._get_submodule_index(basepath)
1125
remaining_path = b'/'.join(parts[i:])
1128
return index, remaining_path
1130
return index, remaining_path
1131
return index, remaining_path
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)
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,
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):
1218
yield (posixpath.join(basepath, path), value)
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)))
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':
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':
1175
1263
def _get_dir_ie(self, path, parent_id):
1176
1264
file_id = self.path2id(path)
1418
1506
return (kind, None, None, None)
1508
def stored_kind(self, relpath):
1509
(index, index_path) = self._lookup_index(relpath.encode('utf-8'))
1513
mode = index[index_path].mode
1517
if S_ISGITLINK(mode):
1518
return 'tree-reference'
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'))
1427
mode = index[index_path].mode
1431
if S_ISGITLINK(mode):
1432
return 'tree-reference'
1524
if self._directory_is_tree_reference(relpath):
1525
return 'tree-reference'