/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

  • Committer: Jelmer Vernooij
  • Date: 2018-11-11 04:08:32 UTC
  • mto: (7143.16.20 even-more-cleanups)
  • mto: This revision was merged to the branch mainline in revision 7175.
  • Revision ID: jelmer@jelmer.uk-20181111040832-nsljjynzzwmznf3h
Run autopep8.

Show diffs side-by-side

added added

removed removed

Lines of Context:
141
141
 
142
142
    def copy(self):
143
143
        ret = self.__class__(
144
 
                self.file_id, self.name, self.parent_id)
 
144
            self.file_id, self.name, self.parent_id)
145
145
        ret.text_sha1 = self.text_sha1
146
146
        ret.text_size = self.text_size
147
147
        ret.executable = self.executable
185
185
 
186
186
    def copy(self):
187
187
        return self.__class__(
188
 
                self.file_id, self.name, self.parent_id,
189
 
                self.symlink_target)
 
188
            self.file_id, self.name, self.parent_id,
 
189
            self.symlink_target)
190
190
 
191
191
 
192
192
class GitTreeSubmodule(_mod_tree.TreeLink):
217
217
 
218
218
    def copy(self):
219
219
        return self.__class__(
220
 
                self.file_id, self.name, self.parent_id,
221
 
                self.reference_revision)
 
220
            self.file_id, self.name, self.parent_id,
 
221
            self.reference_revision)
222
222
 
223
223
 
224
224
entry_factory = {
254
254
        self.store = repository._git.object_store
255
255
        if not isinstance(revision_id, bytes):
256
256
            raise TypeError(revision_id)
257
 
        self.commit_id, self.mapping = repository.lookup_bzr_revision_id(revision_id)
 
257
        self.commit_id, self.mapping = repository.lookup_bzr_revision_id(
 
258
            revision_id)
258
259
        if revision_id == NULL_REVISION:
259
260
            self.tree = None
260
261
            self.mapping = default_mapping
267
268
            except KeyError:
268
269
                raise errors.NoSuchRevision(repository, revision_id)
269
270
            self.tree = commit.tree
270
 
            self._fileid_map = self.mapping.get_fileid_map(self.store.__getitem__, self.tree)
 
271
            self._fileid_map = self.mapping.get_fileid_map(
 
272
                self.store.__getitem__, self.tree)
271
273
 
272
274
    def _get_nested_repository(self, path):
273
275
        nested_repo_transport = self._repository.user_transport.clone(path)
274
 
        nested_controldir = _mod_controldir.ControlDir.open_from_transport(nested_repo_transport)
 
276
        nested_controldir = _mod_controldir.ControlDir.open_from_transport(
 
277
            nested_repo_transport)
275
278
        return nested_controldir.find_repository()
276
279
 
277
280
    def supports_rename_tracking(self):
357
360
            raise errors.NoSuchFile(path)
358
361
        try:
359
362
            (mode, hexsha) = tree_lookup_path(self.store.__getitem__, self.tree,
360
 
                path.encode('utf-8'))
 
363
                                              path.encode('utf-8'))
361
364
        except KeyError:
362
365
            raise errors.NoSuchFile(self, path)
363
366
        else:
391
394
        if from_dir is None:
392
395
            from_dir = u""
393
396
        (store, mode, hexsha) = self._lookup_path(from_dir)
394
 
        if mode is None: # Root
 
397
        if mode is None:  # Root
395
398
            root_ie = self._get_dir_ie(b"", None)
396
399
        else:
397
400
            parent_path = posixpath.dirname(from_dir)
400
403
                root_ie = self._get_dir_ie(from_dir.encode("utf-8"), parent_id)
401
404
            else:
402
405
                root_ie = self._get_file_ie(store, from_dir.encode("utf-8"),
403
 
                    posixpath.basename(from_dir), mode, hexsha)
 
406
                                            posixpath.basename(from_dir), mode, hexsha)
404
407
        if include_root:
405
408
            yield (from_dir, "V", root_ie.kind, root_ie.file_id, root_ie)
406
409
        todo = []
407
410
        if root_ie.kind == 'directory':
408
 
            todo.append((store, from_dir.encode("utf-8"), b"", hexsha, root_ie.file_id))
 
411
            todo.append((store, from_dir.encode("utf-8"),
 
412
                         b"", hexsha, root_ie.file_id))
409
413
        while todo:
410
414
            (store, path, relpath, hexsha, parent_id) = todo.pop()
411
415
            tree = store[hexsha]
417
421
                if stat.S_ISDIR(mode):
418
422
                    ie = self._get_dir_ie(child_path, parent_id)
419
423
                    if recursive:
420
 
                        todo.append((store, child_path, child_relpath, hexsha, ie.file_id))
 
424
                        todo.append(
 
425
                            (store, child_path, child_relpath, hexsha, ie.file_id))
421
426
                else:
422
 
                    ie = self._get_file_ie(store, child_path, name, mode, hexsha, parent_id)
 
427
                    ie = self._get_file_ie(
 
428
                        store, child_path, name, mode, hexsha, parent_id)
423
429
                yield child_relpath.decode('utf-8'), "V", ie.kind, ie.file_id, ie
424
430
 
425
431
    def _get_file_ie(self, store, path, name, mode, hexsha, parent_id):
435
441
        if kind == 'symlink':
436
442
            ie.symlink_target = store[hexsha].data.decode('utf-8')
437
443
        elif kind == 'tree-reference':
438
 
            ie.reference_revision = self.mapping.revision_id_foreign_to_bzr(hexsha)
 
444
            ie.reference_revision = self.mapping.revision_id_foreign_to_bzr(
 
445
                hexsha)
439
446
        else:
440
447
            data = store[hexsha].data
441
448
            ie.text_sha1 = osutils.sha_string(data)
477
484
            if specific_files in ([""], []):
478
485
                specific_files = None
479
486
            else:
480
 
                specific_files = set([p.encode('utf-8') for p in specific_files])
 
487
                specific_files = set([p.encode('utf-8')
 
488
                                      for p in specific_files])
481
489
        todo = deque([(self.store, b"", self.tree, self.get_root_id())])
482
490
        if specific_files is None or u"" in specific_files:
483
491
            yield u"", self._get_dir_ie(b"", None)
492
500
                child_path_decoded = child_path.decode('utf-8')
493
501
                if stat.S_ISDIR(mode):
494
502
                    if (specific_files is None or
495
 
                        any(filter(lambda p: p.startswith(child_path), specific_files))):
 
503
                            any(filter(lambda p: p.startswith(child_path), specific_files))):
496
504
                        extradirs.append(
497
505
                            (store, child_path, hexsha, self.path2id(child_path_decoded)))
498
506
                if specific_files is None or child_path in specific_files:
502
510
                    else:
503
511
                        yield (child_path_decoded,
504
512
                               self._get_file_ie(store, child_path, name, mode,
505
 
                                   hexsha, parent_id))
 
513
                                                 hexsha, parent_id))
506
514
            todo.extendleft(reversed(extradirs))
507
515
 
508
516
    def iter_references(self):
581
589
            return (kind, None, None, None)
582
590
 
583
591
    def find_related_paths_across_trees(self, paths, trees=[],
584
 
            require_versioned=True):
 
592
                                        require_versioned=True):
585
593
        if paths is None:
586
594
            return None
587
595
        if require_versioned:
601
609
        if self.tree is None:
602
610
            return iter([])
603
611
        return self.store.iter_tree_contents(
604
 
                self.tree, include_trees=include_trees)
 
612
            self.tree, include_trees=include_trees)
605
613
 
606
614
    def annotate_iter(self, path, file_id=None,
607
615
                      default_revision=CURRENT_REVISION):
631
639
 
632
640
    def walkdirs(self, prefix=u""):
633
641
        (store, mode, hexsha) = self._lookup_path(prefix)
634
 
        todo = deque([(store, prefix.encode('utf-8'), hexsha, self.path2id(prefix))])
 
642
        todo = deque(
 
643
            [(store, prefix.encode('utf-8'), hexsha, self.path2id(prefix))])
635
644
        while todo:
636
645
            store, path, tree_sha, parent_id = todo.popleft()
637
646
            path_decoded = path.decode('utf-8')
652
661
 
653
662
 
654
663
def tree_delta_from_git_changes(changes, mapping,
655
 
        fileid_maps, specific_files=None,
656
 
        require_versioned=False, include_root=False,
657
 
        target_extras=None):
 
664
                                fileid_maps, specific_files=None,
 
665
                                require_versioned=False, include_root=False,
 
666
                                target_extras=None):
658
667
    """Create a TreeDelta from two git trees.
659
668
 
660
669
    source and target are iterators over tuples with:
691
700
                    (osutils.normalized_filename(newpath)[0], None, mode_kind(newmode)))
692
701
            else:
693
702
                file_id = new_fileid_map.lookup_file_id(newpath_decoded)
694
 
                ret.added.append((newpath_decoded, file_id, mode_kind(newmode)))
 
703
                ret.added.append(
 
704
                    (newpath_decoded, file_id, mode_kind(newmode)))
695
705
        elif newpath is None or newmode == 0:
696
706
            file_id = old_fileid_map.lookup_file_id(oldpath_decoded)
697
707
            ret.removed.append((oldpath_decoded, file_id, mode_kind(oldmode)))
699
709
            file_id = old_fileid_map.lookup_file_id(oldpath_decoded)
700
710
            ret.renamed.append(
701
711
                (oldpath_decoded, newpath.decode('utf-8'), file_id,
702
 
                mode_kind(newmode), (oldsha != newsha),
703
 
                (oldmode != newmode)))
 
712
                 mode_kind(newmode), (oldsha != newsha),
 
713
                 (oldmode != newmode)))
704
714
        elif mode_kind(oldmode) != mode_kind(newmode):
705
715
            file_id = new_fileid_map.lookup_file_id(newpath_decoded)
706
716
            ret.kind_changed.append(
707
717
                (newpath_decoded, file_id, mode_kind(oldmode),
708
 
                mode_kind(newmode)))
 
718
                 mode_kind(newmode)))
709
719
        elif oldsha != newsha or oldmode != newmode:
710
720
            if stat.S_ISDIR(oldmode) and stat.S_ISDIR(newmode):
711
721
                continue
712
722
            file_id = new_fileid_map.lookup_file_id(newpath_decoded)
713
723
            ret.modified.append(
714
724
                (newpath_decoded, file_id, mode_kind(newmode),
715
 
                (oldsha != newsha), (oldmode != newmode)))
 
725
                 (oldsha != newsha), (oldmode != newmode)))
716
726
        else:
717
727
            file_id = new_fileid_map.lookup_file_id(newpath_decoded)
718
 
            ret.unchanged.append((newpath_decoded, file_id, mode_kind(newmode)))
 
728
            ret.unchanged.append(
 
729
                (newpath_decoded, file_id, mode_kind(newmode)))
719
730
 
720
731
    return ret
721
732
 
790
801
                newparent = mapping.generate_file_id(newparentpath)
791
802
        if (not include_unchanged and
792
803
            oldkind == 'directory' and newkind == 'directory' and
793
 
            oldpath_decoded == newpath_decoded):
 
804
                oldpath_decoded == newpath_decoded):
794
805
            continue
795
806
        yield (fileid, (oldpath_decoded, newpath_decoded), (oldsha != newsha),
796
 
             (oldversioned, newversioned),
797
 
             (oldparent, newparent), (oldname, newname),
798
 
             (oldkind, newkind), (oldexe, newexe))
 
807
               (oldversioned, newversioned),
 
808
               (oldparent, newparent), (oldname, newname),
 
809
               (oldkind, newkind), (oldexe, newexe))
799
810
 
800
811
 
801
812
class InterGitTrees(_mod_tree.InterTree):
815
826
                want_unversioned=False):
816
827
        with self.lock_read():
817
828
            changes, target_extras = self._iter_git_changes(
818
 
                    want_unchanged=want_unchanged,
819
 
                    require_versioned=require_versioned,
820
 
                    specific_files=specific_files,
821
 
                    extra_trees=extra_trees,
822
 
                    want_unversioned=want_unversioned)
 
829
                want_unchanged=want_unchanged,
 
830
                require_versioned=require_versioned,
 
831
                specific_files=specific_files,
 
832
                extra_trees=extra_trees,
 
833
                want_unversioned=want_unversioned)
823
834
            source_fileid_map = self.source._fileid_map
824
835
            target_fileid_map = self.target._fileid_map
825
836
            return tree_delta_from_git_changes(changes, self.target.mapping,
826
 
                (source_fileid_map, target_fileid_map),
827
 
                specific_files=specific_files, include_root=include_root,
828
 
                target_extras=target_extras)
 
837
                                               (source_fileid_map,
 
838
                                                target_fileid_map),
 
839
                                               specific_files=specific_files, include_root=include_root,
 
840
                                               target_extras=target_extras)
829
841
 
830
842
    def iter_changes(self, include_unchanged=False, specific_files=None,
831
843
                     pb=None, extra_trees=[], require_versioned=True,
832
844
                     want_unversioned=False):
833
845
        with self.lock_read():
834
846
            changes, target_extras = self._iter_git_changes(
835
 
                    want_unchanged=include_unchanged,
836
 
                    require_versioned=require_versioned,
837
 
                    specific_files=specific_files,
838
 
                    extra_trees=extra_trees,
839
 
                    want_unversioned=want_unversioned)
 
847
                want_unchanged=include_unchanged,
 
848
                require_versioned=require_versioned,
 
849
                specific_files=specific_files,
 
850
                extra_trees=extra_trees,
 
851
                want_unversioned=want_unversioned)
840
852
            return changes_from_git_changes(
841
 
                    changes, self.target.mapping,
842
 
                    specific_files=specific_files,
843
 
                    include_unchanged=include_unchanged,
844
 
                    target_extras=target_extras)
 
853
                changes, self.target.mapping,
 
854
                specific_files=specific_files,
 
855
                include_unchanged=include_unchanged,
 
856
                target_extras=target_extras)
845
857
 
846
858
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
847
 
            require_versioned=False, extra_trees=None,
848
 
            want_unversioned=False):
 
859
                          require_versioned=False, extra_trees=None,
 
860
                          want_unversioned=False):
849
861
        raise NotImplementedError(self._iter_git_changes)
850
862
 
851
863
 
862
874
                isinstance(target, GitRevisionTree))
863
875
 
864
876
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
865
 
            require_versioned=True, extra_trees=None,
866
 
            want_unversioned=False):
 
877
                          require_versioned=True, extra_trees=None,
 
878
                          want_unversioned=False):
867
879
        trees = [self.source]
868
880
        if extra_trees is not None:
869
881
            trees.extend(extra_trees)
870
882
        if specific_files is not None:
871
883
            specific_files = self.target.find_related_paths_across_trees(
872
 
                    specific_files, trees,
873
 
                    require_versioned=require_versioned)
 
884
                specific_files, trees,
 
885
                require_versioned=require_versioned)
874
886
 
875
887
        if self.source._repository._git.object_store != self.target._repository._git.object_store:
876
888
            store = OverlayObjectStore([self.source._repository._git.object_store,
1019
1031
            hexsha = blob.id
1020
1032
        elif kind == "tree-reference":
1021
1033
            if reference_revision is not None:
1022
 
                hexsha = self.branch.lookup_bzr_revision_id(reference_revision)[0]
 
1034
                hexsha = self.branch.lookup_bzr_revision_id(
 
1035
                    reference_revision)[0]
1023
1036
            else:
1024
1037
                hexsha = self._read_submodule_head(path)
1025
1038
                if hexsha is None:
1054
1067
                yield (posixpath.join(basepath, path), value)
1055
1068
                (ctime, mtime, dev, ino, mode, uid, gid, size, sha, flags) = value
1056
1069
                if S_ISGITLINK(mode):
1057
 
                    pass # TODO(jelmer): dive into submodule
1058
 
 
 
1070
                    pass  # TODO(jelmer): dive into submodule
1059
1071
 
1060
1072
    def iter_entries_by_dir(self, specific_files=None, yield_parents=False):
1061
1073
        if yield_parents:
1083
1095
                    continue
1084
1096
                if yield_parents or specific_files is None:
1085
1097
                    for (dir_path, dir_ie) in self._add_missing_parent_ids(parent,
1086
 
                            dir_ids):
 
1098
                                                                           dir_ids):
1087
1099
                        ret[(posixpath.dirname(dir_path), dir_path)] = dir_ie
1088
1100
                file_ie.parent_id = self.path2id(parent)
1089
1101
                ret[(posixpath.dirname(path), path)] = file_ie
1098
1110
    def _get_dir_ie(self, path, parent_id):
1099
1111
        file_id = self.path2id(path)
1100
1112
        return GitTreeDirectory(file_id,
1101
 
            posixpath.basename(path).strip("/"), parent_id)
 
1113
                                posixpath.basename(path).strip("/"), parent_id)
1102
1114
 
1103
1115
    def _get_file_ie(self, name, path, value, parent_id):
1104
1116
        if not isinstance(name, text_type):
1161
1173
            # A directory, perhaps?
1162
1174
            # TODO(jelmer): Deletes that involve submodules?
1163
1175
            for p in list(index):
1164
 
                if p.startswith(subpath+b"/"):
 
1176
                if p.startswith(subpath + b"/"):
1165
1177
                    count += 1
1166
1178
                    self._index_del_entry(index, p)
1167
1179
        else:
1184
1196
        # TODO(jelmer): This shouldn't be called, it's inventory specific.
1185
1197
        for (old_path, new_path, file_id, ie) in delta:
1186
1198
            if old_path is not None:
1187
 
                (index, old_subpath) = self._lookup_index(old_path.encode('utf-8'))
 
1199
                (index, old_subpath) = self._lookup_index(
 
1200
                    old_path.encode('utf-8'))
1188
1201
                if old_subpath in index:
1189
1202
                    self._index_del_entry(index, old_subpath)
1190
1203
                    self._versioned_dirs = None
1199
1212
            to_abs = self.abspath(to_dir)
1200
1213
            if not os.path.isdir(to_abs):
1201
1214
                raise errors.BzrMoveFailedError('', to_dir,
1202
 
                    errors.NotADirectory(to_abs))
 
1215
                                                errors.NotADirectory(to_abs))
1203
1216
 
1204
1217
            for from_rel in from_paths:
1205
1218
                from_tail = os.path.split(from_rel)[-1]
1225
1238
            if after:
1226
1239
                if not self.has_filename(to_rel):
1227
1240
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
1228
 
                        errors.NoSuchFile(to_rel))
 
1241
                                                    errors.NoSuchFile(to_rel))
1229
1242
                if self.basis_tree().is_versioned(to_rel):
1230
1243
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
1231
 
                        errors.AlreadyVersionedError(to_rel))
 
1244
                                                    errors.AlreadyVersionedError(to_rel))
1232
1245
 
1233
1246
                kind = self.kind(to_rel)
1234
1247
            else:
1241
1254
                    exc_type = errors.BzrMoveFailedError
1242
1255
                if self.is_versioned(to_rel):
1243
1256
                    raise exc_type(from_rel, to_rel,
1244
 
                        errors.AlreadyVersionedError(to_rel))
 
1257
                                   errors.AlreadyVersionedError(to_rel))
1245
1258
                if not self.has_filename(from_rel):
1246
1259
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
1247
 
                        errors.NoSuchFile(from_rel))
 
1260
                                                    errors.NoSuchFile(from_rel))
1248
1261
                kind = self.kind(from_rel)
1249
1262
                if not self.is_versioned(from_rel) and kind != 'directory':
1250
1263
                    raise exc_type(from_rel, to_rel,
1251
 
                        errors.NotVersionedError(from_rel))
 
1264
                                   errors.NotVersionedError(from_rel))
1252
1265
                if self.has_filename(to_rel):
1253
1266
                    raise errors.RenameFailedFilesExist(
1254
1267
                        from_rel, to_rel, errors.FileExists(to_rel))
1260
1273
                if from_subpath not in index:
1261
1274
                    # It's not a file
1262
1275
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
1263
 
                        errors.NotVersionedError(path=from_rel))
 
1276
                                                    errors.NotVersionedError(path=from_rel))
1264
1277
 
1265
1278
            if not after:
1266
1279
                try:
1268
1281
                except OSError as e:
1269
1282
                    if e.errno == errno.ENOENT:
1270
1283
                        raise errors.BzrMoveFailedError(from_rel, to_rel,
1271
 
                            errors.NoSuchFile(to_rel))
 
1284
                                                        errors.NoSuchFile(to_rel))
1272
1285
                    raise
1273
1286
            if kind != 'directory':
1274
1287
                (index, from_index_path) = self._lookup_index(from_path)
1278
1291
                    pass
1279
1292
                self._index_add_entry(to_rel, kind)
1280
1293
            else:
1281
 
                todo = [(p, i) for (p, i) in self._recurse_index_entries() if p.startswith(from_path+b'/')]
 
1294
                todo = [(p, i) for (p, i) in self._recurse_index_entries()
 
1295
                        if p.startswith(from_path + b'/')]
1282
1296
                for child_path, child_value in todo:
1283
1297
                    (child_to_index, child_to_index_path) = self._lookup_index(
1284
 
                            posixpath.join(to_path, posixpath.relpath(child_path, from_path)))
 
1298
                        posixpath.join(to_path, posixpath.relpath(child_path, from_path)))
1285
1299
                    child_to_index[child_to_index_path] = child_value
1286
1300
                    # TODO(jelmer): Mark individual index as dirty
1287
1301
                    self._index_dirty = True
1288
 
                    (child_from_index, child_from_index_path) = self._lookup_index(child_path)
1289
 
                    self._index_del_entry(child_from_index, child_from_index_path)
 
1302
                    (child_from_index, child_from_index_path) = self._lookup_index(
 
1303
                        child_path)
 
1304
                    self._index_del_entry(
 
1305
                        child_from_index, child_from_index_path)
1290
1306
 
1291
1307
            self._versioned_dirs = None
1292
1308
            self.flush()
1293
1309
 
1294
1310
    def find_related_paths_across_trees(self, paths, trees=[],
1295
 
            require_versioned=True):
 
1311
                                        require_versioned=True):
1296
1312
        if paths is None:
1297
1313
            return None
1298
1314
 
1369
1385
                isinstance(target, MutableGitIndexTree))
1370
1386
 
1371
1387
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
1372
 
            require_versioned=False, extra_trees=None,
1373
 
            want_unversioned=False):
 
1388
                          require_versioned=False, extra_trees=None,
 
1389
                          want_unversioned=False):
1374
1390
        trees = [self.source]
1375
1391
        if extra_trees is not None:
1376
1392
            trees.extend(extra_trees)
1377
1393
        if specific_files is not None:
1378
1394
            specific_files = self.target.find_related_paths_across_trees(
1379
 
                    specific_files, trees,
1380
 
                    require_versioned=require_versioned)
 
1395
                specific_files, trees,
 
1396
                require_versioned=require_versioned)
1381
1397
        # TODO(jelmer): Restrict to specific_files, for performance reasons.
1382
1398
        with self.lock_read():
1383
1399
            return changes_between_git_tree_and_working_copy(
1390
1406
 
1391
1407
 
1392
1408
def changes_between_git_tree_and_working_copy(store, from_tree_sha, target,
1393
 
        want_unchanged=False, want_unversioned=False):
 
1409
                                              want_unchanged=False, want_unversioned=False):
1394
1410
    """Determine the changes between a git tree and a working tree with index.
1395
1411
 
1396
1412
    """
1425
1441
            if stat.S_ISDIR(st.st_mode):
1426
1442
                blob = Tree()
1427
1443
            else:
1428
 
                blob = blob_from_path_and_stat(target.abspath(e).encode(osutils._fs_enc), st)
 
1444
                blob = blob_from_path_and_stat(
 
1445
                    target.abspath(e).encode(osutils._fs_enc), st)
1429
1446
            store.add_object(blob)
1430
1447
            np = np.encode('utf-8')
1431
1448
            blobs[np] = (blob.id, cleanup_mode(st.st_mode))
1432
1449
            extras.add(np)
1433
 
    to_tree_sha = commit_tree(store, dirified + [(p, s, m) for (p, (s, m)) in blobs.items()])
 
1450
    to_tree_sha = commit_tree(
 
1451
        store, dirified + [(p, s, m) for (p, (s, m)) in blobs.items()])
1434
1452
    return store.tree_changes(
1435
1453
        from_tree_sha, to_tree_sha, include_trees=True,
1436
1454
        want_unchanged=want_unchanged, change_type_same=True), extras