/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: 2019-03-04 00:16:27 UTC
  • mfrom: (7293 work)
  • mto: This revision was merged to the branch mainline in revision 7318.
  • Revision ID: jelmer@jelmer.uk-20190304001627-v6u7o6pf97tukhek
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
48
48
    controldir as _mod_controldir,
49
49
    delta,
50
50
    errors,
51
 
    lock,
52
51
    mutabletree,
53
52
    osutils,
54
53
    revisiontree,
136
135
                self.executable == other.executable)
137
136
 
138
137
    def __repr__(self):
139
 
        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)") % (
140
140
            type(self).__name__, self.file_id, self.name, self.parent_id,
141
141
            self.text_size, self.text_sha1, self.executable)
142
142
 
143
143
    def copy(self):
144
144
        ret = self.__class__(
145
 
                self.file_id, self.name, self.parent_id)
 
145
            self.file_id, self.name, self.parent_id)
146
146
        ret.text_sha1 = self.text_sha1
147
147
        ret.text_size = self.text_size
148
148
        ret.executable = self.executable
186
186
 
187
187
    def copy(self):
188
188
        return self.__class__(
189
 
                self.file_id, self.name, self.parent_id,
190
 
                self.symlink_target)
 
189
            self.file_id, self.name, self.parent_id,
 
190
            self.symlink_target)
191
191
 
192
192
 
193
193
class GitTreeSubmodule(_mod_tree.TreeLink):
205
205
        return 'tree-reference'
206
206
 
207
207
    def __repr__(self):
208
 
        return "%s(file_id=%r, name=%r, parent_id=%r, reference_revision=%r)" % (
 
208
        return ("%s(file_id=%r, name=%r, parent_id=%r, "
 
209
                "reference_revision=%r)") % (
209
210
            type(self).__name__, self.file_id, self.name, self.parent_id,
210
211
            self.reference_revision)
211
212
 
218
219
 
219
220
    def copy(self):
220
221
        return self.__class__(
221
 
                self.file_id, self.name, self.parent_id,
222
 
                self.reference_revision)
 
222
            self.file_id, self.name, self.parent_id,
 
223
            self.reference_revision)
223
224
 
224
225
 
225
226
entry_factory = {
255
256
        self.store = repository._git.object_store
256
257
        if not isinstance(revision_id, bytes):
257
258
            raise TypeError(revision_id)
258
 
        self.commit_id, self.mapping = repository.lookup_bzr_revision_id(revision_id)
 
259
        self.commit_id, self.mapping = repository.lookup_bzr_revision_id(
 
260
            revision_id)
259
261
        if revision_id == NULL_REVISION:
260
262
            self.tree = None
261
263
            self.mapping = default_mapping
268
270
            except KeyError:
269
271
                raise errors.NoSuchRevision(repository, revision_id)
270
272
            self.tree = commit.tree
271
 
            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
275
 
273
276
    def _get_nested_repository(self, path):
274
277
        nested_repo_transport = self._repository.user_transport.clone(path)
275
 
        nested_controldir = _mod_controldir.ControlDir.open_from_transport(nested_repo_transport)
 
278
        nested_controldir = _mod_controldir.ControlDir.open_from_transport(
 
279
            nested_repo_transport)
276
280
        return nested_controldir.find_repository()
277
281
 
278
282
    def supports_rename_tracking(self):
279
283
        return False
280
284
 
281
 
    def get_file_revision(self, path, file_id=None):
 
285
    def get_file_revision(self, path):
282
286
        change_scanner = self._repository._file_change_scanner
283
287
        if self.commit_id == ZERO_SHA:
284
288
            return NULL_REVISION
285
289
        (unused_path, commit_id) = change_scanner.find_last_change_revision(
286
290
            path.encode('utf-8'), self.commit_id)
287
 
        return self._repository.lookup_foreign_revision_id(commit_id, self.mapping)
 
291
        return self._repository.lookup_foreign_revision_id(
 
292
            commit_id, self.mapping)
288
293
 
289
 
    def get_file_mtime(self, path, file_id=None):
 
294
    def get_file_mtime(self, path):
290
295
        try:
291
 
            revid = self.get_file_revision(path, file_id)
 
296
            revid = self.get_file_revision(path)
292
297
        except KeyError:
293
298
            raise errors.NoSuchFile(path)
294
299
        try:
317
322
        return self._fileid_map.lookup_file_id(osutils.safe_unicode(path))
318
323
 
319
324
    def all_file_ids(self):
320
 
        return {self.path2id(path) for path in self.all_versioned_paths()}
 
325
        raise errors.UnsupportedOperation(self.all_file_ids, self)
321
326
 
322
327
    def all_versioned_paths(self):
323
328
        ret = {u''}
341
346
 
342
347
    def has_or_had_id(self, file_id):
343
348
        try:
344
 
            path = self.id2path(file_id)
 
349
            self.id2path(file_id)
345
350
        except errors.NoSuchId:
346
351
            return False
347
352
        return True
357
362
        if self.tree is None:
358
363
            raise errors.NoSuchFile(path)
359
364
        try:
360
 
            (mode, hexsha) = tree_lookup_path(self.store.__getitem__, self.tree,
361
 
                path.encode('utf-8'))
 
365
            (mode, hexsha) = tree_lookup_path(
 
366
                self.store.__getitem__, self.tree, path.encode('utf-8'))
362
367
        except KeyError:
363
368
            raise errors.NoSuchFile(self, path)
364
369
        else:
365
370
            return (self.store, mode, hexsha)
366
371
 
367
 
    def is_executable(self, path, file_id=None):
 
372
    def is_executable(self, path):
368
373
        (store, mode, hexsha) = self._lookup_path(path)
369
374
        if mode is None:
370
375
            # the tree root is a directory
371
376
            return False
372
377
        return mode_is_executable(mode)
373
378
 
374
 
    def kind(self, path, file_id=None):
 
379
    def kind(self, path):
375
380
        (store, mode, hexsha) = self._lookup_path(path)
376
381
        if mode is None:
377
382
            # the tree root is a directory
389
394
    def list_files(self, include_root=False, from_dir=None, recursive=True):
390
395
        if self.tree is None:
391
396
            return
392
 
        if from_dir is None:
 
397
        if from_dir is None or from_dir == '.':
393
398
            from_dir = u""
394
399
        (store, mode, hexsha) = self._lookup_path(from_dir)
395
 
        if mode is None: # Root
 
400
        if mode is None:  # Root
396
401
            root_ie = self._get_dir_ie(b"", None)
397
402
        else:
398
403
            parent_path = posixpath.dirname(from_dir)
400
405
            if mode_kind(mode) == 'directory':
401
406
                root_ie = self._get_dir_ie(from_dir.encode("utf-8"), parent_id)
402
407
            else:
403
 
                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"),
404
410
                    posixpath.basename(from_dir), mode, hexsha)
405
411
        if include_root:
406
 
            yield (from_dir, "V", root_ie.kind, root_ie.file_id, root_ie)
 
412
            yield (from_dir, "V", root_ie.kind, root_ie)
407
413
        todo = []
408
414
        if root_ie.kind == 'directory':
409
 
            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
417
        while todo:
411
418
            (store, path, relpath, hexsha, parent_id) = todo.pop()
412
419
            tree = store[hexsha]
418
425
                if stat.S_ISDIR(mode):
419
426
                    ie = self._get_dir_ie(child_path, parent_id)
420
427
                    if recursive:
421
 
                        todo.append((store, child_path, child_relpath, hexsha, ie.file_id))
 
428
                        todo.append(
 
429
                            (store, child_path, child_relpath, hexsha,
 
430
                             ie.file_id))
422
431
                else:
423
 
                    ie = self._get_file_ie(store, child_path, name, mode, hexsha, parent_id)
424
 
                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)
425
435
 
426
436
    def _get_file_ie(self, store, path, name, mode, hexsha, parent_id):
427
437
        if not isinstance(path, bytes):
436
446
        if kind == 'symlink':
437
447
            ie.symlink_target = store[hexsha].data.decode('utf-8')
438
448
        elif kind == 'tree-reference':
439
 
            ie.reference_revision = self.mapping.revision_id_foreign_to_bzr(hexsha)
 
449
            ie.reference_revision = self.mapping.revision_id_foreign_to_bzr(
 
450
                hexsha)
440
451
        else:
441
452
            data = store[hexsha].data
442
453
            ie.text_sha1 = osutils.sha_string(data)
449
460
        file_id = self._fileid_map.lookup_file_id(path)
450
461
        return GitTreeDirectory(file_id, posixpath.basename(path), parent_id)
451
462
 
452
 
    def iter_child_entries(self, path, file_id=None):
 
463
    def iter_child_entries(self, path):
453
464
        (store, mode, tree_sha) = self._lookup_path(path)
454
465
 
455
466
        if mode is not None and not stat.S_ISDIR(mode):
478
489
            if specific_files in ([""], []):
479
490
                specific_files = None
480
491
            else:
481
 
                specific_files = set([p.encode('utf-8') for p in specific_files])
 
492
                specific_files = set([p.encode('utf-8')
 
493
                                      for p in specific_files])
482
494
        todo = deque([(self.store, b"", self.tree, self.get_root_id())])
483
495
        if specific_files is None or u"" in specific_files:
484
496
            yield u"", self._get_dir_ie(b"", None)
493
505
                child_path_decoded = child_path.decode('utf-8')
494
506
                if stat.S_ISDIR(mode):
495
507
                    if (specific_files is None or
496
 
                        any(filter(lambda p: p.startswith(child_path), specific_files))):
 
508
                            any([p for p in specific_files if p.startswith(
 
509
                                child_path)])):
497
510
                        extradirs.append(
498
 
                            (store, child_path, hexsha, self.path2id(child_path_decoded)))
 
511
                            (store, child_path, hexsha,
 
512
                             self.path2id(child_path_decoded)))
499
513
                if specific_files is None or child_path in specific_files:
500
514
                    if stat.S_ISDIR(mode):
501
515
                        yield (child_path_decoded,
503
517
                    else:
504
518
                        yield (child_path_decoded,
505
519
                               self._get_file_ie(store, child_path, name, mode,
506
 
                                   hexsha, parent_id))
 
520
                                                 hexsha, parent_id))
507
521
            todo.extendleft(reversed(extradirs))
508
522
 
509
523
    def iter_references(self):
516
530
        """See RevisionTree.get_revision_id."""
517
531
        return self._revision_id
518
532
 
519
 
    def get_file_sha1(self, path, file_id=None, stat_value=None):
 
533
    def get_file_sha1(self, path, stat_value=None):
520
534
        if self.tree is None:
521
535
            raise errors.NoSuchFile(path)
522
 
        return osutils.sha_string(self.get_file_text(path, file_id))
 
536
        return osutils.sha_string(self.get_file_text(path))
523
537
 
524
 
    def get_file_verifier(self, path, file_id=None, stat_value=None):
 
538
    def get_file_verifier(self, path, stat_value=None):
525
539
        (store, mode, hexsha) = self._lookup_path(path)
526
540
        return ("GIT", hexsha)
527
541
 
528
 
    def get_file_size(self, path, file_id=None):
 
542
    def get_file_size(self, path):
529
543
        (store, mode, hexsha) = self._lookup_path(path)
530
544
        if stat.S_ISREG(mode):
531
545
            return len(store[hexsha].data)
532
546
        return None
533
547
 
534
 
    def get_file_text(self, path, file_id=None):
 
548
    def get_file_text(self, path):
535
549
        """See RevisionTree.get_file_text."""
536
550
        (store, mode, hexsha) = self._lookup_path(path)
537
551
        if stat.S_ISREG(mode):
539
553
        else:
540
554
            return b""
541
555
 
542
 
    def get_symlink_target(self, path, file_id=None):
 
556
    def get_symlink_target(self, path):
543
557
        """See RevisionTree.get_symlink_target."""
544
558
        (store, mode, hexsha) = self._lookup_path(path)
545
559
        if stat.S_ISLNK(mode):
547
561
        else:
548
562
            return None
549
563
 
550
 
    def get_reference_revision(self, path, file_id=None):
 
564
    def get_reference_revision(self, path):
551
565
        """See RevisionTree.get_symlink_target."""
552
566
        (store, mode, hexsha) = self._lookup_path(path)
553
567
        if S_ISGITLINK(mode):
571
585
        if kind == 'file':
572
586
            executable = mode_is_executable(mode)
573
587
            contents = store[hexsha].data
574
 
            return (kind, len(contents), executable, osutils.sha_string(contents))
 
588
            return (kind, len(contents), executable,
 
589
                    osutils.sha_string(contents))
575
590
        elif kind == 'symlink':
576
591
            return (kind, None, None, store[hexsha].data.decode('utf-8'))
577
592
        elif kind == 'tree-reference':
582
597
            return (kind, None, None, None)
583
598
 
584
599
    def find_related_paths_across_trees(self, paths, trees=[],
585
 
            require_versioned=True):
 
600
                                        require_versioned=True):
586
601
        if paths is None:
587
602
            return None
588
603
        if require_versioned:
602
617
        if self.tree is None:
603
618
            return iter([])
604
619
        return self.store.iter_tree_contents(
605
 
                self.tree, include_trees=include_trees)
 
620
            self.tree, include_trees=include_trees)
606
621
 
607
 
    def annotate_iter(self, path, file_id=None,
608
 
                      default_revision=CURRENT_REVISION):
 
622
    def annotate_iter(self, path, default_revision=CURRENT_REVISION):
609
623
        """Return an iterator of revision_id, line tuples.
610
624
 
611
625
        For working trees (and mutable trees in general), the special
612
626
        revision_id 'current:' will be used for lines that are new in this
613
627
        tree, e.g. uncommitted changes.
614
 
        :param file_id: The file to produce an annotated version from
615
628
        :param default_revision: For lines that don't match a basis, mark them
616
629
            with this revision id. Not all implementations will make use of
617
630
            this value.
632
645
 
633
646
    def walkdirs(self, prefix=u""):
634
647
        (store, mode, hexsha) = self._lookup_path(prefix)
635
 
        todo = deque([(store, prefix.encode('utf-8'), hexsha, self.path2id(prefix))])
 
648
        todo = deque(
 
649
            [(store, prefix.encode('utf-8'), hexsha, self.path2id(prefix))])
636
650
        while todo:
637
651
            store, path, tree_sha, parent_id = todo.popleft()
638
652
            path_decoded = path.decode('utf-8')
653
667
 
654
668
 
655
669
def tree_delta_from_git_changes(changes, mapping,
656
 
        fileid_maps, specific_files=None,
657
 
        require_versioned=False, include_root=False,
658
 
        target_extras=None):
 
670
                                fileid_maps, specific_files=None,
 
671
                                require_versioned=False, include_root=False,
 
672
                                target_extras=None):
659
673
    """Create a TreeDelta from two git trees.
660
674
 
661
675
    source and target are iterators over tuples with:
665
679
    if target_extras is None:
666
680
        target_extras = set()
667
681
    ret = delta.TreeDelta()
 
682
    added = []
668
683
    for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes:
669
684
        if newpath == b'' and not include_root:
670
685
            continue
671
686
        if oldpath is None:
672
 
            oldpath_encoded = None
 
687
            oldpath_decoded = None
673
688
        else:
674
689
            oldpath_decoded = oldpath.decode('utf-8')
675
690
        if newpath is None:
677
692
        else:
678
693
            newpath_decoded = newpath.decode('utf-8')
679
694
        if not (specific_files is None or
680
 
                (oldpath is not None and osutils.is_inside_or_parent_of_any(specific_files, oldpath_decoded)) or
681
 
                (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))):
682
701
            continue
683
702
        if mapping.is_special_file(oldpath):
684
703
            oldpath = None
687
706
        if oldpath is None and newpath is None:
688
707
            continue
689
708
        if oldpath is None:
690
 
            if newpath in target_extras:
691
 
                ret.unversioned.append(
692
 
                    (osutils.normalized_filename(newpath)[0], None, mode_kind(newmode)))
693
 
            else:
694
 
                file_id = new_fileid_map.lookup_file_id(newpath_decoded)
695
 
                ret.added.append((newpath_decoded, file_id, mode_kind(newmode)))
 
709
            added.append((newpath, mode_kind(newmode)))
696
710
        elif newpath is None or newmode == 0:
697
711
            file_id = old_fileid_map.lookup_file_id(oldpath_decoded)
698
712
            ret.removed.append((oldpath_decoded, file_id, mode_kind(oldmode)))
700
714
            file_id = old_fileid_map.lookup_file_id(oldpath_decoded)
701
715
            ret.renamed.append(
702
716
                (oldpath_decoded, newpath.decode('utf-8'), file_id,
703
 
                mode_kind(newmode), (oldsha != newsha),
704
 
                (oldmode != newmode)))
 
717
                 mode_kind(newmode), (oldsha != newsha),
 
718
                 (oldmode != newmode)))
705
719
        elif mode_kind(oldmode) != mode_kind(newmode):
706
720
            file_id = new_fileid_map.lookup_file_id(newpath_decoded)
707
721
            ret.kind_changed.append(
708
722
                (newpath_decoded, file_id, mode_kind(oldmode),
709
 
                mode_kind(newmode)))
 
723
                 mode_kind(newmode)))
710
724
        elif oldsha != newsha or oldmode != newmode:
711
725
            if stat.S_ISDIR(oldmode) and stat.S_ISDIR(newmode):
712
726
                continue
713
727
            file_id = new_fileid_map.lookup_file_id(newpath_decoded)
714
728
            ret.modified.append(
715
729
                (newpath_decoded, file_id, mode_kind(newmode),
716
 
                (oldsha != newsha), (oldmode != newmode)))
 
730
                 (oldsha != newsha), (oldmode != newmode)))
717
731
        else:
718
732
            file_id = new_fileid_map.lookup_file_id(newpath_decoded)
719
 
            ret.unchanged.append((newpath_decoded, file_id, mode_kind(newmode)))
 
733
            ret.unchanged.append(
 
734
                (newpath_decoded, file_id, mode_kind(newmode)))
 
735
 
 
736
    implicit_dirs = {b''}
 
737
    for path, kind in added:
 
738
        if kind == 'directory' or path in target_extras:
 
739
            continue
 
740
        implicit_dirs.update(osutils.parent_directories(path))
 
741
 
 
742
    for path, kind in added:
 
743
        if kind == 'directory' and path not in implicit_dirs:
 
744
            continue
 
745
        path_decoded = osutils.normalized_filename(path)[0]
 
746
        if path in target_extras:
 
747
            ret.unversioned.append((path_decoded, None, kind))
 
748
        else:
 
749
            file_id = new_fileid_map.lookup_file_id(path_decoded)
 
750
            ret.added.append((path_decoded, file_id, kind))
720
751
 
721
752
    return ret
722
753
 
723
754
 
724
 
def changes_from_git_changes(changes, mapping, specific_files=None, include_unchanged=False,
725
 
                             target_extras=None):
 
755
def changes_from_git_changes(changes, mapping, specific_files=None,
 
756
                             include_unchanged=False, target_extras=None):
726
757
    """Create a iter_changes-like generator from a git stream.
727
758
 
728
759
    source and target are iterators over tuples with:
740
771
        else:
741
772
            newpath_decoded = None
742
773
        if not (specific_files is None or
743
 
                (oldpath_decoded is not None and osutils.is_inside_or_parent_of_any(specific_files, oldpath_decoded)) or
744
 
                (newpath_decoded is not None and osutils.is_inside_or_parent_of_any(specific_files, newpath_decoded))):
 
774
                (oldpath_decoded is not None and
 
775
                    osutils.is_inside_or_parent_of_any(
 
776
                        specific_files, oldpath_decoded)) or
 
777
                (newpath_decoded is not None and
 
778
                    osutils.is_inside_or_parent_of_any(
 
779
                        specific_files, newpath_decoded))):
745
780
            continue
746
781
        if oldpath is not None and mapping.is_special_file(oldpath):
747
782
            continue
791
826
                newparent = mapping.generate_file_id(newparentpath)
792
827
        if (not include_unchanged and
793
828
            oldkind == 'directory' and newkind == 'directory' and
794
 
            oldpath_decoded == newpath_decoded):
 
829
                oldpath_decoded == newpath_decoded):
795
830
            continue
796
831
        yield (fileid, (oldpath_decoded, newpath_decoded), (oldsha != newsha),
797
 
             (oldversioned, newversioned),
798
 
             (oldparent, newparent), (oldname, newname),
799
 
             (oldkind, newkind), (oldexe, newexe))
 
832
               (oldversioned, newversioned),
 
833
               (oldparent, newparent), (oldname, newname),
 
834
               (oldkind, newkind), (oldexe, newexe))
800
835
 
801
836
 
802
837
class InterGitTrees(_mod_tree.InterTree):
816
851
                want_unversioned=False):
817
852
        with self.lock_read():
818
853
            changes, target_extras = self._iter_git_changes(
819
 
                    want_unchanged=want_unchanged,
820
 
                    require_versioned=require_versioned,
821
 
                    specific_files=specific_files,
822
 
                    extra_trees=extra_trees,
823
 
                    want_unversioned=want_unversioned)
 
854
                want_unchanged=want_unchanged,
 
855
                require_versioned=require_versioned,
 
856
                specific_files=specific_files,
 
857
                extra_trees=extra_trees,
 
858
                want_unversioned=want_unversioned)
824
859
            source_fileid_map = self.source._fileid_map
825
860
            target_fileid_map = self.target._fileid_map
826
 
            return tree_delta_from_git_changes(changes, self.target.mapping,
 
861
            return tree_delta_from_git_changes(
 
862
                changes, self.target.mapping,
827
863
                (source_fileid_map, target_fileid_map),
828
 
                specific_files=specific_files, include_root=include_root,
829
 
                target_extras=target_extras)
 
864
                specific_files=specific_files,
 
865
                include_root=include_root, target_extras=target_extras)
830
866
 
831
867
    def iter_changes(self, include_unchanged=False, specific_files=None,
832
868
                     pb=None, extra_trees=[], require_versioned=True,
833
869
                     want_unversioned=False):
834
870
        with self.lock_read():
835
871
            changes, target_extras = self._iter_git_changes(
836
 
                    want_unchanged=include_unchanged,
837
 
                    require_versioned=require_versioned,
838
 
                    specific_files=specific_files,
839
 
                    extra_trees=extra_trees,
840
 
                    want_unversioned=want_unversioned)
 
872
                want_unchanged=include_unchanged,
 
873
                require_versioned=require_versioned,
 
874
                specific_files=specific_files,
 
875
                extra_trees=extra_trees,
 
876
                want_unversioned=want_unversioned)
841
877
            return changes_from_git_changes(
842
 
                    changes, self.target.mapping,
843
 
                    specific_files=specific_files,
844
 
                    include_unchanged=include_unchanged,
845
 
                    target_extras=target_extras)
 
878
                changes, self.target.mapping,
 
879
                specific_files=specific_files,
 
880
                include_unchanged=include_unchanged,
 
881
                target_extras=target_extras)
846
882
 
847
883
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
848
 
            require_versioned=False, extra_trees=None,
849
 
            want_unversioned=False):
 
884
                          require_versioned=False, extra_trees=None,
 
885
                          want_unversioned=False):
850
886
        raise NotImplementedError(self._iter_git_changes)
851
887
 
852
888
 
863
899
                isinstance(target, GitRevisionTree))
864
900
 
865
901
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
866
 
            require_versioned=True, extra_trees=None,
867
 
            want_unversioned=False):
 
902
                          require_versioned=True, extra_trees=None,
 
903
                          want_unversioned=False):
868
904
        trees = [self.source]
869
905
        if extra_trees is not None:
870
906
            trees.extend(extra_trees)
871
907
        if specific_files is not None:
872
908
            specific_files = self.target.find_related_paths_across_trees(
873
 
                    specific_files, trees,
874
 
                    require_versioned=require_versioned)
 
909
                specific_files, trees,
 
910
                require_versioned=require_versioned)
875
911
 
876
 
        if self.source._repository._git.object_store != self.target._repository._git.object_store:
877
 
            store = OverlayObjectStore([self.source._repository._git.object_store,
878
 
                                        self.target._repository._git.object_store])
 
912
        if (self.source._repository._git.object_store !=
 
913
                self.target._repository._git.object_store):
 
914
            store = OverlayObjectStore(
 
915
                [self.source._repository._git.object_store,
 
916
                    self.target._repository._git.object_store])
879
917
        else:
880
918
            store = self.source._repository._git.object_store
881
 
        return self.source._repository._git.object_store.tree_changes(
 
919
        return store.tree_changes(
882
920
            self.source.tree, self.target.tree, want_unchanged=want_unchanged,
883
921
            include_trees=True, change_type_same=True), set()
884
922
 
930
968
        with self.lock_read():
931
969
            path = path.rstrip('/')
932
970
            if self.is_versioned(path.rstrip('/')):
933
 
                return self._fileid_map.lookup_file_id(osutils.safe_unicode(path))
 
971
                return self._fileid_map.lookup_file_id(
 
972
                    osutils.safe_unicode(path))
934
973
            return None
935
974
 
936
975
    def has_id(self, file_id):
956
995
            raise errors.NoSuchId(self, file_id)
957
996
 
958
997
    def _set_root_id(self, file_id):
959
 
        self._fileid_map.set_file_id("", file_id)
 
998
        raise errors.UnsupportedOperation(self._set_root_id, self)
960
999
 
961
1000
    def get_root_id(self):
962
1001
        return self.path2id(u"")
993
1032
            try:
994
1033
                file, stat_val = self.get_file_with_stat(path)
995
1034
            except (errors.NoSuchFile, IOError):
996
 
                # TODO: Rather than come up with something here, use the old index
 
1035
                # TODO: Rather than come up with something here, use the old
 
1036
                # index
997
1037
                file = BytesIO()
998
1038
                stat_val = os.stat_result(
999
1039
                    (stat.S_IFREG | 0o644, 0, 0, 0, 0, 0, 0, 0, 0, 0))
1000
1040
            with file:
1001
1041
                blob.set_raw_string(file.read())
1002
1042
            # Add object to the repository if it didn't exist yet
1003
 
            if not blob.id in self.store:
 
1043
            if blob.id not in self.store:
1004
1044
                self.store.add_object(blob)
1005
1045
            hexsha = blob.id
1006
1046
        elif kind == "symlink":
1015
1055
            blob.set_raw_string(
1016
1056
                self.get_symlink_target(path).encode("utf-8"))
1017
1057
            # Add object to the repository if it didn't exist yet
1018
 
            if not blob.id in self.store:
 
1058
            if blob.id not in self.store:
1019
1059
                self.store.add_object(blob)
1020
1060
            hexsha = blob.id
1021
1061
        elif kind == "tree-reference":
1022
1062
            if reference_revision is not None:
1023
 
                hexsha = self.branch.lookup_bzr_revision_id(reference_revision)[0]
 
1063
                hexsha = self.branch.lookup_bzr_revision_id(
 
1064
                    reference_revision)[0]
1024
1065
            else:
1025
1066
                hexsha = self._read_submodule_head(path)
1026
1067
                if hexsha is None:
1053
1094
                index = self.index
1054
1095
            for path, value in index.items():
1055
1096
                yield (posixpath.join(basepath, path), value)
1056
 
                (ctime, mtime, dev, ino, mode, uid, gid, size, sha, flags) = value
 
1097
                (ctime, mtime, dev, ino, mode, uid, gid, size, sha,
 
1098
                 flags) = value
1057
1099
                if S_ISGITLINK(mode):
1058
 
                    pass # TODO(jelmer): dive into submodule
1059
 
 
 
1100
                    pass  # TODO(jelmer): dive into submodule
1060
1101
 
1061
1102
    def iter_entries_by_dir(self, specific_files=None, yield_parents=False):
1062
1103
        if yield_parents:
1075
1116
                if self.mapping.is_special_file(path):
1076
1117
                    continue
1077
1118
                path = path.decode("utf-8")
1078
 
                if specific_files is not None and not path in specific_files:
 
1119
                if specific_files is not None and path not in specific_files:
1079
1120
                    continue
1080
1121
                (parent, name) = posixpath.split(path)
1081
1122
                try:
1083
1124
                except errors.NoSuchFile:
1084
1125
                    continue
1085
1126
                if yield_parents or specific_files is None:
1086
 
                    for (dir_path, dir_ie) in self._add_missing_parent_ids(parent,
1087
 
                            dir_ids):
 
1127
                    for (dir_path, dir_ie) in self._add_missing_parent_ids(
 
1128
                            parent, dir_ids):
1088
1129
                        ret[(posixpath.dirname(dir_path), dir_path)] = dir_ie
1089
1130
                file_ie.parent_id = self.path2id(parent)
1090
1131
                ret[(posixpath.dirname(path), path)] = file_ie
1099
1140
    def _get_dir_ie(self, path, parent_id):
1100
1141
        file_id = self.path2id(path)
1101
1142
        return GitTreeDirectory(file_id,
1102
 
            posixpath.basename(path).strip("/"), parent_id)
 
1143
                                posixpath.basename(path).strip("/"), parent_id)
1103
1144
 
1104
1145
    def _get_file_ie(self, name, path, value, parent_id):
1105
1146
        if not isinstance(name, text_type):
1115
1156
        kind = mode_kind(mode)
1116
1157
        ie = entry_factory[kind](file_id, name, parent_id)
1117
1158
        if kind == 'symlink':
1118
 
            ie.symlink_target = self.get_symlink_target(path, file_id)
 
1159
            ie.symlink_target = self.get_symlink_target(path)
1119
1160
        elif kind == 'tree-reference':
1120
 
            ie.reference_revision = self.get_reference_revision(path, file_id)
 
1161
            ie.reference_revision = self.get_reference_revision(path)
1121
1162
        else:
1122
1163
            try:
1123
 
                data = self.get_file_text(path, file_id)
 
1164
                data = self.get_file_text(path)
1124
1165
            except errors.NoSuchFile:
1125
1166
                data = None
1126
1167
            except IOError as e:
1162
1203
            # A directory, perhaps?
1163
1204
            # TODO(jelmer): Deletes that involve submodules?
1164
1205
            for p in list(index):
1165
 
                if p.startswith(subpath+b"/"):
 
1206
                if p.startswith(subpath + b"/"):
1166
1207
                    count += 1
1167
1208
                    self._index_del_entry(index, p)
1168
1209
        else:
1170
1211
        self._versioned_dirs = None
1171
1212
        return count
1172
1213
 
1173
 
    def unversion(self, paths, file_ids=None):
 
1214
    def unversion(self, paths):
1174
1215
        with self.lock_tree_write():
1175
1216
            for path in paths:
1176
1217
                if self._unversion_path(path) == 0:
1185
1226
        # TODO(jelmer): This shouldn't be called, it's inventory specific.
1186
1227
        for (old_path, new_path, file_id, ie) in delta:
1187
1228
            if old_path is not None:
1188
 
                (index, old_subpath) = self._lookup_index(old_path.encode('utf-8'))
 
1229
                (index, old_subpath) = self._lookup_index(
 
1230
                    old_path.encode('utf-8'))
1189
1231
                if old_subpath in index:
1190
1232
                    self._index_del_entry(index, old_subpath)
1191
1233
                    self._versioned_dirs = None
1200
1242
            to_abs = self.abspath(to_dir)
1201
1243
            if not os.path.isdir(to_abs):
1202
1244
                raise errors.BzrMoveFailedError('', to_dir,
1203
 
                    errors.NotADirectory(to_abs))
 
1245
                                                errors.NotADirectory(to_abs))
1204
1246
 
1205
1247
            for from_rel in from_paths:
1206
1248
                from_tail = os.path.split(from_rel)[-1]
1225
1267
                    not self.is_versioned(to_rel))
1226
1268
            if after:
1227
1269
                if not self.has_filename(to_rel):
1228
 
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
1229
 
                        errors.NoSuchFile(to_rel))
 
1270
                    raise errors.BzrMoveFailedError(
 
1271
                        from_rel, to_rel, errors.NoSuchFile(to_rel))
1230
1272
                if self.basis_tree().is_versioned(to_rel):
1231
 
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
1232
 
                        errors.AlreadyVersionedError(to_rel))
 
1273
                    raise errors.BzrMoveFailedError(
 
1274
                        from_rel, to_rel, errors.AlreadyVersionedError(to_rel))
1233
1275
 
1234
1276
                kind = self.kind(to_rel)
1235
1277
            else:
1242
1284
                    exc_type = errors.BzrMoveFailedError
1243
1285
                if self.is_versioned(to_rel):
1244
1286
                    raise exc_type(from_rel, to_rel,
1245
 
                        errors.AlreadyVersionedError(to_rel))
 
1287
                                   errors.AlreadyVersionedError(to_rel))
1246
1288
                if not self.has_filename(from_rel):
1247
 
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
1248
 
                        errors.NoSuchFile(from_rel))
 
1289
                    raise errors.BzrMoveFailedError(
 
1290
                        from_rel, to_rel, errors.NoSuchFile(from_rel))
1249
1291
                kind = self.kind(from_rel)
1250
1292
                if not self.is_versioned(from_rel) and kind != 'directory':
1251
1293
                    raise exc_type(from_rel, to_rel,
1252
 
                        errors.NotVersionedError(from_rel))
 
1294
                                   errors.NotVersionedError(from_rel))
1253
1295
                if self.has_filename(to_rel):
1254
1296
                    raise errors.RenameFailedFilesExist(
1255
1297
                        from_rel, to_rel, errors.FileExists(to_rel))
1260
1302
                (index, from_subpath) = self._lookup_index(from_path)
1261
1303
                if from_subpath not in index:
1262
1304
                    # It's not a file
1263
 
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
 
1305
                    raise errors.BzrMoveFailedError(
 
1306
                        from_rel, to_rel,
1264
1307
                        errors.NotVersionedError(path=from_rel))
1265
1308
 
1266
1309
            if not after:
1268
1311
                    self._rename_one(from_rel, to_rel)
1269
1312
                except OSError as e:
1270
1313
                    if e.errno == errno.ENOENT:
1271
 
                        raise errors.BzrMoveFailedError(from_rel, to_rel,
1272
 
                            errors.NoSuchFile(to_rel))
 
1314
                        raise errors.BzrMoveFailedError(
 
1315
                            from_rel, to_rel, errors.NoSuchFile(to_rel))
1273
1316
                    raise
1274
1317
            if kind != 'directory':
1275
1318
                (index, from_index_path) = self._lookup_index(from_path)
1279
1322
                    pass
1280
1323
                self._index_add_entry(to_rel, kind)
1281
1324
            else:
1282
 
                todo = [(p, i) for (p, i) in self._recurse_index_entries() if p.startswith(from_path+b'/')]
 
1325
                todo = [(p, i) for (p, i) in self._recurse_index_entries()
 
1326
                        if p.startswith(from_path + b'/')]
1283
1327
                for child_path, child_value in todo:
1284
1328
                    (child_to_index, child_to_index_path) = self._lookup_index(
1285
 
                            posixpath.join(to_path, posixpath.relpath(child_path, from_path)))
 
1329
                        posixpath.join(to_path, posixpath.relpath(child_path, from_path)))
1286
1330
                    child_to_index[child_to_index_path] = child_value
1287
1331
                    # TODO(jelmer): Mark individual index as dirty
1288
1332
                    self._index_dirty = True
1289
 
                    (child_from_index, child_from_index_path) = self._lookup_index(child_path)
1290
 
                    self._index_del_entry(child_from_index, child_from_index_path)
 
1333
                    (child_from_index, child_from_index_path) = self._lookup_index(
 
1334
                        child_path)
 
1335
                    self._index_del_entry(
 
1336
                        child_from_index, child_from_index_path)
1291
1337
 
1292
1338
            self._versioned_dirs = None
1293
1339
            self.flush()
1294
1340
 
1295
1341
    def find_related_paths_across_trees(self, paths, trees=[],
1296
 
            require_versioned=True):
 
1342
                                        require_versioned=True):
1297
1343
        if paths is None:
1298
1344
            return None
1299
1345
 
1336
1382
        else:
1337
1383
            return (kind, None, None, None)
1338
1384
 
1339
 
    def kind(self, relpath, file_id=None):
 
1385
    def kind(self, relpath):
1340
1386
        kind = osutils.file_kind(self.abspath(relpath))
1341
1387
        if kind == 'directory':
1342
1388
            (index, index_path) = self._lookup_index(relpath.encode('utf-8'))
1370
1416
                isinstance(target, MutableGitIndexTree))
1371
1417
 
1372
1418
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
1373
 
            require_versioned=False, extra_trees=None,
1374
 
            want_unversioned=False):
 
1419
                          require_versioned=False, extra_trees=None,
 
1420
                          want_unversioned=False):
1375
1421
        trees = [self.source]
1376
1422
        if extra_trees is not None:
1377
1423
            trees.extend(extra_trees)
1378
1424
        if specific_files is not None:
1379
1425
            specific_files = self.target.find_related_paths_across_trees(
1380
 
                    specific_files, trees,
1381
 
                    require_versioned=require_versioned)
 
1426
                specific_files, trees,
 
1427
                require_versioned=require_versioned)
1382
1428
        # TODO(jelmer): Restrict to specific_files, for performance reasons.
1383
1429
        with self.lock_read():
1384
1430
            return changes_between_git_tree_and_working_copy(
1391
1437
 
1392
1438
 
1393
1439
def changes_between_git_tree_and_working_copy(store, from_tree_sha, target,
1394
 
        want_unchanged=False, want_unversioned=False):
 
1440
                                              want_unchanged=False,
 
1441
                                              want_unversioned=False):
1395
1442
    """Determine the changes between a git tree and a working tree with index.
1396
1443
 
1397
1444
    """
1409
1456
                # Entry was removed; keep it listed, but mark it as gone.
1410
1457
                blobs[path] = (ZERO_SHA, 0)
1411
1458
            elif e.errno == errno.EISDIR:
1412
 
                # Entry was turned into a directory
1413
 
                dirified.append((path, Tree().id, stat.S_IFDIR))
1414
 
                store.add_object(Tree())
 
1459
                # TODO(jelmer): Only do this if 'path' appears in .gitmodules?
 
1460
                if S_ISGITLINK(index_entry.mode):
 
1461
                    blobs[path] = (index_entry.sha, index_entry.mode)
 
1462
                else:
 
1463
                    # Entry was turned into a directory
 
1464
                    dirified.append((path, Tree().id, stat.S_IFDIR))
 
1465
                    store.add_object(Tree())
1415
1466
            else:
1416
1467
                raise
1417
1468
        else:
1433
1484
            if stat.S_ISDIR(st.st_mode):
1434
1485
                blob = Tree()
1435
1486
            else:
1436
 
                blob = blob_from_path_and_stat(target.abspath(e).encode(osutils._fs_enc), st)
 
1487
                blob = blob_from_path_and_stat(
 
1488
                    target.abspath(e).encode(osutils._fs_enc), st)
1437
1489
            store.add_object(blob)
1438
1490
            np = np.encode('utf-8')
1439
1491
            blobs[np] = (blob.id, cleanup_mode(st.st_mode))
1440
1492
            extras.add(np)
1441
 
    to_tree_sha = commit_tree(store, dirified + [(p, s, m) for (p, (s, m)) in blobs.items()])
 
1493
    to_tree_sha = commit_tree(
 
1494
        store, dirified + [(p, s, m) for (p, (s, m)) in blobs.items()])
1442
1495
    return store.tree_changes(
1443
1496
        from_tree_sha, to_tree_sha, include_trees=True,
1444
1497
        want_unchanged=want_unchanged, change_type_same=True), extras