135
135
self.executable == other.executable)
137
137
def __repr__(self):
138
return "%s(file_id=%r, name=%r, parent_id=%r, text_size=%r, text_sha1=%r, executable=%r)" % (
138
return ("%s(file_id=%r, name=%r, parent_id=%r, text_size=%r, "
139
"text_sha1=%r, executable=%r)") % (
139
140
type(self).__name__, self.file_id, self.name, self.parent_id,
140
141
self.text_size, self.text_sha1, self.executable)
143
144
ret = self.__class__(
144
self.file_id, self.name, self.parent_id)
145
self.file_id, self.name, self.parent_id)
145
146
ret.text_sha1 = self.text_sha1
146
147
ret.text_size = self.text_size
147
148
ret.executable = self.executable
268
271
raise errors.NoSuchRevision(repository, revision_id)
269
272
self.tree = commit.tree
270
self._fileid_map = self.mapping.get_fileid_map(self.store.__getitem__, self.tree)
273
self._fileid_map = self.mapping.get_fileid_map(
274
self.store.__getitem__, self.tree)
272
276
def _get_nested_repository(self, path):
273
277
nested_repo_transport = self._repository.user_transport.clone(path)
274
nested_controldir = _mod_controldir.ControlDir.open_from_transport(nested_repo_transport)
278
nested_controldir = _mod_controldir.ControlDir.open_from_transport(
279
nested_repo_transport)
275
280
return nested_controldir.find_repository()
277
282
def supports_rename_tracking(self):
399
405
if mode_kind(mode) == 'directory':
400
406
root_ie = self._get_dir_ie(from_dir.encode("utf-8"), parent_id)
402
root_ie = self._get_file_ie(store, from_dir.encode("utf-8"),
408
root_ie = self._get_file_ie(
409
store, from_dir.encode("utf-8"),
403
410
posixpath.basename(from_dir), mode, hexsha)
405
412
yield (from_dir, "V", root_ie.kind, root_ie.file_id, root_ie)
407
414
if root_ie.kind == 'directory':
408
todo.append((store, from_dir.encode("utf-8"), b"", hexsha, root_ie.file_id))
415
todo.append((store, from_dir.encode("utf-8"),
416
b"", hexsha, root_ie.file_id))
410
418
(store, path, relpath, hexsha, parent_id) = todo.pop()
411
419
tree = store[hexsha]
417
425
if stat.S_ISDIR(mode):
418
426
ie = self._get_dir_ie(child_path, parent_id)
420
todo.append((store, child_path, child_relpath, hexsha, ie.file_id))
429
(store, child_path, child_relpath, hexsha,
422
ie = self._get_file_ie(store, child_path, name, mode, hexsha, parent_id)
423
yield child_relpath.decode('utf-8'), "V", ie.kind, ie.file_id, ie
432
ie = self._get_file_ie(
433
store, child_path, name, mode, hexsha, parent_id)
434
yield (child_relpath.decode('utf-8'), "V", ie.kind, ie.file_id,
425
437
def _get_file_ie(self, store, path, name, mode, hexsha, parent_id):
426
438
if not isinstance(path, bytes):
477
490
if specific_files in ([""], []):
478
491
specific_files = None
480
specific_files = set([p.encode('utf-8') for p in specific_files])
493
specific_files = set([p.encode('utf-8')
494
for p in specific_files])
481
495
todo = deque([(self.store, b"", self.tree, self.get_root_id())])
482
496
if specific_files is None or u"" in specific_files:
483
497
yield u"", self._get_dir_ie(b"", None)
492
506
child_path_decoded = child_path.decode('utf-8')
493
507
if stat.S_ISDIR(mode):
494
508
if (specific_files is None or
495
any(filter(lambda p: p.startswith(child_path), specific_files))):
509
any([p for p in specific_files if p.startswith(
496
511
extradirs.append(
497
(store, child_path, hexsha, self.path2id(child_path_decoded)))
512
(store, child_path, hexsha,
513
self.path2id(child_path_decoded)))
498
514
if specific_files is None or child_path in specific_files:
499
515
if stat.S_ISDIR(mode):
500
516
yield (child_path_decoded,
570
586
if kind == 'file':
571
587
executable = mode_is_executable(mode)
572
588
contents = store[hexsha].data
573
return (kind, len(contents), executable, osutils.sha_string(contents))
589
return (kind, len(contents), executable,
590
osutils.sha_string(contents))
574
591
elif kind == 'symlink':
575
592
return (kind, None, None, store[hexsha].data.decode('utf-8'))
576
593
elif kind == 'tree-reference':
630
647
def walkdirs(self, prefix=u""):
631
648
(store, mode, hexsha) = self._lookup_path(prefix)
632
todo = deque([(store, prefix.encode('utf-8'), hexsha, self.path2id(prefix))])
650
[(store, prefix.encode('utf-8'), hexsha, self.path2id(prefix))])
634
652
store, path, tree_sha, parent_id = todo.popleft()
635
653
path_decoded = path.decode('utf-8')
652
670
def tree_delta_from_git_changes(changes, mapping,
653
fileid_maps, specific_files=None,
654
require_versioned=False, include_root=False,
671
fileid_maps, specific_files=None,
672
require_versioned=False, include_root=False,
656
674
"""Create a TreeDelta from two git trees.
658
676
source and target are iterators over tuples with:
675
693
newpath_decoded = newpath.decode('utf-8')
676
694
if not (specific_files is None or
677
(oldpath is not None and osutils.is_inside_or_parent_of_any(specific_files, oldpath_decoded)) or
678
(newpath is not None and osutils.is_inside_or_parent_of_any(specific_files, newpath_decoded))):
695
(oldpath is not None and
696
osutils.is_inside_or_parent_of_any(
697
specific_files, oldpath_decoded)) or
698
(newpath is not None and
699
osutils.is_inside_or_parent_of_any(
700
specific_files, newpath_decoded))):
680
702
if mapping.is_special_file(oldpath):
686
708
if oldpath is None:
687
709
if newpath in target_extras:
688
710
ret.unversioned.append(
689
(osutils.normalized_filename(newpath)[0], None, mode_kind(newmode)))
711
(osutils.normalized_filename(newpath)[0], None,
691
714
file_id = new_fileid_map.lookup_file_id(newpath_decoded)
692
ret.added.append((newpath_decoded, file_id, mode_kind(newmode)))
716
(newpath_decoded, file_id, mode_kind(newmode)))
693
717
elif newpath is None or newmode == 0:
694
718
file_id = old_fileid_map.lookup_file_id(oldpath_decoded)
695
719
ret.removed.append((oldpath_decoded, file_id, mode_kind(oldmode)))
697
721
file_id = old_fileid_map.lookup_file_id(oldpath_decoded)
698
722
ret.renamed.append(
699
723
(oldpath_decoded, newpath.decode('utf-8'), file_id,
700
mode_kind(newmode), (oldsha != newsha),
701
(oldmode != newmode)))
724
mode_kind(newmode), (oldsha != newsha),
725
(oldmode != newmode)))
702
726
elif mode_kind(oldmode) != mode_kind(newmode):
703
727
file_id = new_fileid_map.lookup_file_id(newpath_decoded)
704
728
ret.kind_changed.append(
705
729
(newpath_decoded, file_id, mode_kind(oldmode),
707
731
elif oldsha != newsha or oldmode != newmode:
708
732
if stat.S_ISDIR(oldmode) and stat.S_ISDIR(newmode):
710
734
file_id = new_fileid_map.lookup_file_id(newpath_decoded)
711
735
ret.modified.append(
712
736
(newpath_decoded, file_id, mode_kind(newmode),
713
(oldsha != newsha), (oldmode != newmode)))
737
(oldsha != newsha), (oldmode != newmode)))
715
739
file_id = new_fileid_map.lookup_file_id(newpath_decoded)
716
ret.unchanged.append((newpath_decoded, file_id, mode_kind(newmode)))
740
ret.unchanged.append(
741
(newpath_decoded, file_id, mode_kind(newmode)))
721
def changes_from_git_changes(changes, mapping, specific_files=None, include_unchanged=False,
746
def changes_from_git_changes(changes, mapping, specific_files=None,
747
include_unchanged=False, target_extras=None):
723
748
"""Create a iter_changes-like generator from a git stream.
725
750
source and target are iterators over tuples with:
738
763
newpath_decoded = None
739
764
if not (specific_files is None or
740
(oldpath_decoded is not None and osutils.is_inside_or_parent_of_any(specific_files, oldpath_decoded)) or
741
(newpath_decoded is not None and osutils.is_inside_or_parent_of_any(specific_files, newpath_decoded))):
765
(oldpath_decoded is not None and
766
osutils.is_inside_or_parent_of_any(
767
specific_files, oldpath_decoded)) or
768
(newpath_decoded is not None and
769
osutils.is_inside_or_parent_of_any(
770
specific_files, newpath_decoded))):
743
772
if oldpath is not None and mapping.is_special_file(oldpath):
788
817
newparent = mapping.generate_file_id(newparentpath)
789
818
if (not include_unchanged and
790
819
oldkind == 'directory' and newkind == 'directory' and
791
oldpath_decoded == newpath_decoded):
820
oldpath_decoded == newpath_decoded):
793
822
yield (fileid, (oldpath_decoded, newpath_decoded), (oldsha != newsha),
794
(oldversioned, newversioned),
795
(oldparent, newparent), (oldname, newname),
796
(oldkind, newkind), (oldexe, newexe))
823
(oldversioned, newversioned),
824
(oldparent, newparent), (oldname, newname),
825
(oldkind, newkind), (oldexe, newexe))
799
828
class InterGitTrees(_mod_tree.InterTree):
813
842
want_unversioned=False):
814
843
with self.lock_read():
815
844
changes, target_extras = self._iter_git_changes(
816
want_unchanged=want_unchanged,
817
require_versioned=require_versioned,
818
specific_files=specific_files,
819
extra_trees=extra_trees,
820
want_unversioned=want_unversioned)
845
want_unchanged=want_unchanged,
846
require_versioned=require_versioned,
847
specific_files=specific_files,
848
extra_trees=extra_trees,
849
want_unversioned=want_unversioned)
821
850
source_fileid_map = self.source._fileid_map
822
851
target_fileid_map = self.target._fileid_map
823
return tree_delta_from_git_changes(changes, self.target.mapping,
852
return tree_delta_from_git_changes(
853
changes, self.target.mapping,
824
854
(source_fileid_map, target_fileid_map),
825
specific_files=specific_files, include_root=include_root,
826
target_extras=target_extras)
855
specific_files=specific_files,
856
include_root=include_root, target_extras=target_extras)
828
858
def iter_changes(self, include_unchanged=False, specific_files=None,
829
859
pb=None, extra_trees=[], require_versioned=True,
830
860
want_unversioned=False):
831
861
with self.lock_read():
832
862
changes, target_extras = self._iter_git_changes(
833
want_unchanged=include_unchanged,
834
require_versioned=require_versioned,
835
specific_files=specific_files,
836
extra_trees=extra_trees,
837
want_unversioned=want_unversioned)
863
want_unchanged=include_unchanged,
864
require_versioned=require_versioned,
865
specific_files=specific_files,
866
extra_trees=extra_trees,
867
want_unversioned=want_unversioned)
838
868
return changes_from_git_changes(
839
changes, self.target.mapping,
840
specific_files=specific_files,
841
include_unchanged=include_unchanged,
842
target_extras=target_extras)
869
changes, self.target.mapping,
870
specific_files=specific_files,
871
include_unchanged=include_unchanged,
872
target_extras=target_extras)
844
874
def _iter_git_changes(self, want_unchanged=False, specific_files=None,
845
require_versioned=False, extra_trees=None,
846
want_unversioned=False):
875
require_versioned=False, extra_trees=None,
876
want_unversioned=False):
847
877
raise NotImplementedError(self._iter_git_changes)
860
890
isinstance(target, GitRevisionTree))
862
892
def _iter_git_changes(self, want_unchanged=False, specific_files=None,
863
require_versioned=True, extra_trees=None,
864
want_unversioned=False):
893
require_versioned=True, extra_trees=None,
894
want_unversioned=False):
865
895
trees = [self.source]
866
896
if extra_trees is not None:
867
897
trees.extend(extra_trees)
868
898
if specific_files is not None:
869
899
specific_files = self.target.find_related_paths_across_trees(
870
specific_files, trees,
871
require_versioned=require_versioned)
900
specific_files, trees,
901
require_versioned=require_versioned)
873
if self.source._repository._git.object_store != self.target._repository._git.object_store:
874
store = OverlayObjectStore([self.source._repository._git.object_store,
875
self.target._repository._git.object_store])
903
if (self.source._repository._git.object_store !=
904
self.target._repository._git.object_store):
905
store = OverlayObjectStore(
906
[self.source._repository._git.object_store,
907
self.target._repository._git.object_store])
877
909
store = self.source._repository._git.object_store
878
return self.source._repository._git.object_store.tree_changes(
910
return store.tree_changes(
879
911
self.source.tree, self.target.tree, want_unchanged=want_unchanged,
880
912
include_trees=True, change_type_same=True), set()
991
1024
file, stat_val = self.get_file_with_stat(path)
992
1025
except (errors.NoSuchFile, IOError):
993
# TODO: Rather than come up with something here, use the old index
1026
# TODO: Rather than come up with something here, use the old
994
1028
file = BytesIO()
995
1029
stat_val = os.stat_result(
996
1030
(stat.S_IFREG | 0o644, 0, 0, 0, 0, 0, 0, 0, 0, 0))
998
1032
blob.set_raw_string(file.read())
999
1033
# Add object to the repository if it didn't exist yet
1000
if not blob.id in self.store:
1034
if blob.id not in self.store:
1001
1035
self.store.add_object(blob)
1002
1036
hexsha = blob.id
1003
1037
elif kind == "symlink":
1012
1046
blob.set_raw_string(
1013
1047
self.get_symlink_target(path).encode("utf-8"))
1014
1048
# Add object to the repository if it didn't exist yet
1015
if not blob.id in self.store:
1049
if blob.id not in self.store:
1016
1050
self.store.add_object(blob)
1017
1051
hexsha = blob.id
1018
1052
elif kind == "tree-reference":
1019
1053
if reference_revision is not None:
1020
hexsha = self.branch.lookup_bzr_revision_id(reference_revision)[0]
1054
hexsha = self.branch.lookup_bzr_revision_id(
1055
reference_revision)[0]
1022
1057
hexsha = self._read_submodule_head(path)
1023
1058
if hexsha is None:
1050
1085
index = self.index
1051
1086
for path, value in index.items():
1052
1087
yield (posixpath.join(basepath, path), value)
1053
(ctime, mtime, dev, ino, mode, uid, gid, size, sha, flags) = value
1088
(ctime, mtime, dev, ino, mode, uid, gid, size, sha,
1054
1090
if S_ISGITLINK(mode):
1055
pass # TODO(jelmer): dive into submodule
1091
pass # TODO(jelmer): dive into submodule
1058
1093
def iter_entries_by_dir(self, specific_files=None, yield_parents=False):
1059
1094
if yield_parents:
1080
1115
except errors.NoSuchFile:
1082
1117
if yield_parents or specific_files is None:
1083
for (dir_path, dir_ie) in self._add_missing_parent_ids(parent,
1118
for (dir_path, dir_ie) in self._add_missing_parent_ids(
1085
1120
ret[(posixpath.dirname(dir_path), dir_path)] = dir_ie
1086
1121
file_ie.parent_id = self.path2id(parent)
1087
1122
ret[(posixpath.dirname(path), path)] = file_ie
1096
1131
def _get_dir_ie(self, path, parent_id):
1097
1132
file_id = self.path2id(path)
1098
1133
return GitTreeDirectory(file_id,
1099
posixpath.basename(path).strip("/"), parent_id)
1134
posixpath.basename(path).strip("/"), parent_id)
1101
1136
def _get_file_ie(self, name, path, value, parent_id):
1102
1137
if not isinstance(name, text_type):
1182
1217
# TODO(jelmer): This shouldn't be called, it's inventory specific.
1183
1218
for (old_path, new_path, file_id, ie) in delta:
1184
1219
if old_path is not None:
1185
(index, old_subpath) = self._lookup_index(old_path.encode('utf-8'))
1220
(index, old_subpath) = self._lookup_index(
1221
old_path.encode('utf-8'))
1186
1222
if old_subpath in index:
1187
1223
self._index_del_entry(index, old_subpath)
1188
1224
self._versioned_dirs = None
1222
1258
not self.is_versioned(to_rel))
1224
1260
if not self.has_filename(to_rel):
1225
raise errors.BzrMoveFailedError(from_rel, to_rel,
1226
errors.NoSuchFile(to_rel))
1261
raise errors.BzrMoveFailedError(
1262
from_rel, to_rel, errors.NoSuchFile(to_rel))
1227
1263
if self.basis_tree().is_versioned(to_rel):
1228
raise errors.BzrMoveFailedError(from_rel, to_rel,
1229
errors.AlreadyVersionedError(to_rel))
1264
raise errors.BzrMoveFailedError(
1265
from_rel, to_rel, errors.AlreadyVersionedError(to_rel))
1231
1267
kind = self.kind(to_rel)
1239
1275
exc_type = errors.BzrMoveFailedError
1240
1276
if self.is_versioned(to_rel):
1241
1277
raise exc_type(from_rel, to_rel,
1242
errors.AlreadyVersionedError(to_rel))
1278
errors.AlreadyVersionedError(to_rel))
1243
1279
if not self.has_filename(from_rel):
1244
raise errors.BzrMoveFailedError(from_rel, to_rel,
1245
errors.NoSuchFile(from_rel))
1280
raise errors.BzrMoveFailedError(
1281
from_rel, to_rel, errors.NoSuchFile(from_rel))
1246
1282
kind = self.kind(from_rel)
1247
1283
if not self.is_versioned(from_rel) and kind != 'directory':
1248
1284
raise exc_type(from_rel, to_rel,
1249
errors.NotVersionedError(from_rel))
1285
errors.NotVersionedError(from_rel))
1250
1286
if self.has_filename(to_rel):
1251
1287
raise errors.RenameFailedFilesExist(
1252
1288
from_rel, to_rel, errors.FileExists(to_rel))
1265
1302
self._rename_one(from_rel, to_rel)
1266
1303
except OSError as e:
1267
1304
if e.errno == errno.ENOENT:
1268
raise errors.BzrMoveFailedError(from_rel, to_rel,
1269
errors.NoSuchFile(to_rel))
1305
raise errors.BzrMoveFailedError(
1306
from_rel, to_rel, errors.NoSuchFile(to_rel))
1271
1308
if kind != 'directory':
1272
1309
(index, from_index_path) = self._lookup_index(from_path)
1277
1314
self._index_add_entry(to_rel, kind)
1279
todo = [(p, i) for (p, i) in self._recurse_index_entries() if p.startswith(from_path+b'/')]
1316
todo = [(p, i) for (p, i) in self._recurse_index_entries()
1317
if p.startswith(from_path + b'/')]
1280
1318
for child_path, child_value in todo:
1281
1319
(child_to_index, child_to_index_path) = self._lookup_index(
1282
posixpath.join(to_path, posixpath.relpath(child_path, from_path)))
1320
posixpath.join(to_path, posixpath.relpath(child_path, from_path)))
1283
1321
child_to_index[child_to_index_path] = child_value
1284
1322
# TODO(jelmer): Mark individual index as dirty
1285
1323
self._index_dirty = True
1286
(child_from_index, child_from_index_path) = self._lookup_index(child_path)
1287
self._index_del_entry(child_from_index, child_from_index_path)
1324
(child_from_index, child_from_index_path) = self._lookup_index(
1326
self._index_del_entry(
1327
child_from_index, child_from_index_path)
1289
1329
self._versioned_dirs = None
1292
1332
def find_related_paths_across_trees(self, paths, trees=[],
1293
require_versioned=True):
1333
require_versioned=True):
1294
1334
if paths is None:
1367
1407
isinstance(target, MutableGitIndexTree))
1369
1409
def _iter_git_changes(self, want_unchanged=False, specific_files=None,
1370
require_versioned=False, extra_trees=None,
1371
want_unversioned=False):
1410
require_versioned=False, extra_trees=None,
1411
want_unversioned=False):
1372
1412
trees = [self.source]
1373
1413
if extra_trees is not None:
1374
1414
trees.extend(extra_trees)
1375
1415
if specific_files is not None:
1376
1416
specific_files = self.target.find_related_paths_across_trees(
1377
specific_files, trees,
1378
require_versioned=require_versioned)
1417
specific_files, trees,
1418
require_versioned=require_versioned)
1379
1419
# TODO(jelmer): Restrict to specific_files, for performance reasons.
1380
1420
with self.lock_read():
1381
1421
return changes_between_git_tree_and_working_copy(
1423
1464
if stat.S_ISDIR(st.st_mode):
1426
blob = blob_from_path_and_stat(target.abspath(e).encode(osutils._fs_enc), st)
1467
blob = blob_from_path_and_stat(
1468
target.abspath(e).encode(osutils._fs_enc), st)
1427
1469
store.add_object(blob)
1428
1470
np = np.encode('utf-8')
1429
1471
blobs[np] = (blob.id, cleanup_mode(st.st_mode))
1431
to_tree_sha = commit_tree(store, dirified + [(p, s, m) for (p, (s, m)) in blobs.items()])
1473
to_tree_sha = commit_tree(
1474
store, dirified + [(p, s, m) for (p, (s, m)) in blobs.items()])
1432
1475
return store.tree_changes(
1433
1476
from_tree_sha, to_tree_sha, include_trees=True,
1434
1477
want_unchanged=want_unchanged, change_type_same=True), extras