/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-16 23:15:15 UTC
  • mfrom: (7180 work)
  • mto: This revision was merged to the branch mainline in revision 7183.
  • Revision ID: jelmer@jelmer.uk-20181116231515-zqd2yn6kj8lfydyp
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:
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
412
            yield (from_dir, "V", root_ie.kind, root_ie.file_id, 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.file_id,
 
435
                       ie)
425
436
 
426
437
    def _get_file_ie(self, store, path, name, mode, hexsha, parent_id):
427
438
        if not isinstance(path, bytes):
436
447
        if kind == 'symlink':
437
448
            ie.symlink_target = store[hexsha].data.decode('utf-8')
438
449
        elif kind == 'tree-reference':
439
 
            ie.reference_revision = self.mapping.revision_id_foreign_to_bzr(hexsha)
 
450
            ie.reference_revision = self.mapping.revision_id_foreign_to_bzr(
 
451
                hexsha)
440
452
        else:
441
453
            data = store[hexsha].data
442
454
            ie.text_sha1 = osutils.sha_string(data)
478
490
            if specific_files in ([""], []):
479
491
                specific_files = None
480
492
            else:
481
 
                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])
482
495
        todo = deque([(self.store, b"", self.tree, self.get_root_id())])
483
496
        if specific_files is None or u"" in specific_files:
484
497
            yield u"", self._get_dir_ie(b"", None)
493
506
                child_path_decoded = child_path.decode('utf-8')
494
507
                if stat.S_ISDIR(mode):
495
508
                    if (specific_files is None or
496
 
                        any(filter(lambda p: p.startswith(child_path), specific_files))):
 
509
                            any([p for p in specific_files if p.startswith(
 
510
                                child_path)])):
497
511
                        extradirs.append(
498
 
                            (store, child_path, hexsha, self.path2id(child_path_decoded)))
 
512
                            (store, child_path, hexsha,
 
513
                             self.path2id(child_path_decoded)))
499
514
                if specific_files is None or child_path in specific_files:
500
515
                    if stat.S_ISDIR(mode):
501
516
                        yield (child_path_decoded,
503
518
                    else:
504
519
                        yield (child_path_decoded,
505
520
                               self._get_file_ie(store, child_path, name, mode,
506
 
                                   hexsha, parent_id))
 
521
                                                 hexsha, parent_id))
507
522
            todo.extendleft(reversed(extradirs))
508
523
 
509
524
    def iter_references(self):
516
531
        """See RevisionTree.get_revision_id."""
517
532
        return self._revision_id
518
533
 
519
 
    def get_file_sha1(self, path, file_id=None, stat_value=None):
 
534
    def get_file_sha1(self, path, stat_value=None):
520
535
        if self.tree is None:
521
536
            raise errors.NoSuchFile(path)
522
 
        return osutils.sha_string(self.get_file_text(path, file_id))
 
537
        return osutils.sha_string(self.get_file_text(path))
523
538
 
524
 
    def get_file_verifier(self, path, file_id=None, stat_value=None):
 
539
    def get_file_verifier(self, path, stat_value=None):
525
540
        (store, mode, hexsha) = self._lookup_path(path)
526
541
        return ("GIT", hexsha)
527
542
 
528
 
    def get_file_size(self, path, file_id=None):
 
543
    def get_file_size(self, path):
529
544
        (store, mode, hexsha) = self._lookup_path(path)
530
545
        if stat.S_ISREG(mode):
531
546
            return len(store[hexsha].data)
532
547
        return None
533
548
 
534
 
    def get_file_text(self, path, file_id=None):
 
549
    def get_file_text(self, path):
535
550
        """See RevisionTree.get_file_text."""
536
551
        (store, mode, hexsha) = self._lookup_path(path)
537
552
        if stat.S_ISREG(mode):
539
554
        else:
540
555
            return b""
541
556
 
542
 
    def get_symlink_target(self, path, file_id=None):
 
557
    def get_symlink_target(self, path):
543
558
        """See RevisionTree.get_symlink_target."""
544
559
        (store, mode, hexsha) = self._lookup_path(path)
545
560
        if stat.S_ISLNK(mode):
547
562
        else:
548
563
            return None
549
564
 
550
 
    def get_reference_revision(self, path, file_id=None):
 
565
    def get_reference_revision(self, path):
551
566
        """See RevisionTree.get_symlink_target."""
552
567
        (store, mode, hexsha) = self._lookup_path(path)
553
568
        if S_ISGITLINK(mode):
571
586
        if kind == 'file':
572
587
            executable = mode_is_executable(mode)
573
588
            contents = store[hexsha].data
574
 
            return (kind, len(contents), executable, osutils.sha_string(contents))
 
589
            return (kind, len(contents), executable,
 
590
                    osutils.sha_string(contents))
575
591
        elif kind == 'symlink':
576
592
            return (kind, None, None, store[hexsha].data.decode('utf-8'))
577
593
        elif kind == 'tree-reference':
582
598
            return (kind, None, None, None)
583
599
 
584
600
    def find_related_paths_across_trees(self, paths, trees=[],
585
 
            require_versioned=True):
 
601
                                        require_versioned=True):
586
602
        if paths is None:
587
603
            return None
588
604
        if require_versioned:
602
618
        if self.tree is None:
603
619
            return iter([])
604
620
        return self.store.iter_tree_contents(
605
 
                self.tree, include_trees=include_trees)
 
621
            self.tree, include_trees=include_trees)
606
622
 
607
 
    def annotate_iter(self, path, file_id=None,
608
 
                      default_revision=CURRENT_REVISION):
 
623
    def annotate_iter(self, path, default_revision=CURRENT_REVISION):
609
624
        """Return an iterator of revision_id, line tuples.
610
625
 
611
626
        For working trees (and mutable trees in general), the special
612
627
        revision_id 'current:' will be used for lines that are new in this
613
628
        tree, e.g. uncommitted changes.
614
 
        :param file_id: The file to produce an annotated version from
615
629
        :param default_revision: For lines that don't match a basis, mark them
616
630
            with this revision id. Not all implementations will make use of
617
631
            this value.
632
646
 
633
647
    def walkdirs(self, prefix=u""):
634
648
        (store, mode, hexsha) = self._lookup_path(prefix)
635
 
        todo = deque([(store, prefix.encode('utf-8'), hexsha, self.path2id(prefix))])
 
649
        todo = deque(
 
650
            [(store, prefix.encode('utf-8'), hexsha, self.path2id(prefix))])
636
651
        while todo:
637
652
            store, path, tree_sha, parent_id = todo.popleft()
638
653
            path_decoded = path.decode('utf-8')
653
668
 
654
669
 
655
670
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):
 
671
                                fileid_maps, specific_files=None,
 
672
                                require_versioned=False, include_root=False,
 
673
                                target_extras=None):
659
674
    """Create a TreeDelta from two git trees.
660
675
 
661
676
    source and target are iterators over tuples with:
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
689
708
        if oldpath is None:
690
709
            if newpath in target_extras:
691
710
                ret.unversioned.append(
692
 
                    (osutils.normalized_filename(newpath)[0], None, mode_kind(newmode)))
 
711
                    (osutils.normalized_filename(newpath)[0], None,
 
712
                     mode_kind(newmode)))
693
713
            else:
694
714
                file_id = new_fileid_map.lookup_file_id(newpath_decoded)
695
 
                ret.added.append((newpath_decoded, file_id, mode_kind(newmode)))
 
715
                ret.added.append(
 
716
                    (newpath_decoded, file_id, mode_kind(newmode)))
696
717
        elif newpath is None or newmode == 0:
697
718
            file_id = old_fileid_map.lookup_file_id(oldpath_decoded)
698
719
            ret.removed.append((oldpath_decoded, file_id, mode_kind(oldmode)))
700
721
            file_id = old_fileid_map.lookup_file_id(oldpath_decoded)
701
722
            ret.renamed.append(
702
723
                (oldpath_decoded, newpath.decode('utf-8'), file_id,
703
 
                mode_kind(newmode), (oldsha != newsha),
704
 
                (oldmode != newmode)))
 
724
                 mode_kind(newmode), (oldsha != newsha),
 
725
                 (oldmode != newmode)))
705
726
        elif mode_kind(oldmode) != mode_kind(newmode):
706
727
            file_id = new_fileid_map.lookup_file_id(newpath_decoded)
707
728
            ret.kind_changed.append(
708
729
                (newpath_decoded, file_id, mode_kind(oldmode),
709
 
                mode_kind(newmode)))
 
730
                 mode_kind(newmode)))
710
731
        elif oldsha != newsha or oldmode != newmode:
711
732
            if stat.S_ISDIR(oldmode) and stat.S_ISDIR(newmode):
712
733
                continue
713
734
            file_id = new_fileid_map.lookup_file_id(newpath_decoded)
714
735
            ret.modified.append(
715
736
                (newpath_decoded, file_id, mode_kind(newmode),
716
 
                (oldsha != newsha), (oldmode != newmode)))
 
737
                 (oldsha != newsha), (oldmode != newmode)))
717
738
        else:
718
739
            file_id = new_fileid_map.lookup_file_id(newpath_decoded)
719
 
            ret.unchanged.append((newpath_decoded, file_id, mode_kind(newmode)))
 
740
            ret.unchanged.append(
 
741
                (newpath_decoded, file_id, mode_kind(newmode)))
720
742
 
721
743
    return ret
722
744
 
723
745
 
724
 
def changes_from_git_changes(changes, mapping, specific_files=None, include_unchanged=False,
725
 
                             target_extras=None):
 
746
def changes_from_git_changes(changes, mapping, specific_files=None,
 
747
                             include_unchanged=False, target_extras=None):
726
748
    """Create a iter_changes-like generator from a git stream.
727
749
 
728
750
    source and target are iterators over tuples with:
740
762
        else:
741
763
            newpath_decoded = None
742
764
        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))):
 
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))):
745
771
            continue
746
772
        if oldpath is not None and mapping.is_special_file(oldpath):
747
773
            continue
791
817
                newparent = mapping.generate_file_id(newparentpath)
792
818
        if (not include_unchanged and
793
819
            oldkind == 'directory' and newkind == 'directory' and
794
 
            oldpath_decoded == newpath_decoded):
 
820
                oldpath_decoded == newpath_decoded):
795
821
            continue
796
822
        yield (fileid, (oldpath_decoded, newpath_decoded), (oldsha != newsha),
797
 
             (oldversioned, newversioned),
798
 
             (oldparent, newparent), (oldname, newname),
799
 
             (oldkind, newkind), (oldexe, newexe))
 
823
               (oldversioned, newversioned),
 
824
               (oldparent, newparent), (oldname, newname),
 
825
               (oldkind, newkind), (oldexe, newexe))
800
826
 
801
827
 
802
828
class InterGitTrees(_mod_tree.InterTree):
816
842
                want_unversioned=False):
817
843
        with self.lock_read():
818
844
            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)
 
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)
824
850
            source_fileid_map = self.source._fileid_map
825
851
            target_fileid_map = self.target._fileid_map
826
 
            return tree_delta_from_git_changes(changes, self.target.mapping,
 
852
            return tree_delta_from_git_changes(
 
853
                changes, self.target.mapping,
827
854
                (source_fileid_map, target_fileid_map),
828
 
                specific_files=specific_files, include_root=include_root,
829
 
                target_extras=target_extras)
 
855
                specific_files=specific_files,
 
856
                include_root=include_root, target_extras=target_extras)
830
857
 
831
858
    def iter_changes(self, include_unchanged=False, specific_files=None,
832
859
                     pb=None, extra_trees=[], require_versioned=True,
833
860
                     want_unversioned=False):
834
861
        with self.lock_read():
835
862
            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)
 
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)
841
868
            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)
 
869
                changes, self.target.mapping,
 
870
                specific_files=specific_files,
 
871
                include_unchanged=include_unchanged,
 
872
                target_extras=target_extras)
846
873
 
847
874
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
848
 
            require_versioned=False, extra_trees=None,
849
 
            want_unversioned=False):
 
875
                          require_versioned=False, extra_trees=None,
 
876
                          want_unversioned=False):
850
877
        raise NotImplementedError(self._iter_git_changes)
851
878
 
852
879
 
863
890
                isinstance(target, GitRevisionTree))
864
891
 
865
892
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
866
 
            require_versioned=True, extra_trees=None,
867
 
            want_unversioned=False):
 
893
                          require_versioned=True, extra_trees=None,
 
894
                          want_unversioned=False):
868
895
        trees = [self.source]
869
896
        if extra_trees is not None:
870
897
            trees.extend(extra_trees)
871
898
        if specific_files is not None:
872
899
            specific_files = self.target.find_related_paths_across_trees(
873
 
                    specific_files, trees,
874
 
                    require_versioned=require_versioned)
 
900
                specific_files, trees,
 
901
                require_versioned=require_versioned)
875
902
 
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])
 
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])
879
908
        else:
880
909
            store = self.source._repository._git.object_store
881
 
        return self.source._repository._git.object_store.tree_changes(
 
910
        return store.tree_changes(
882
911
            self.source.tree, self.target.tree, want_unchanged=want_unchanged,
883
912
            include_trees=True, change_type_same=True), set()
884
913
 
930
959
        with self.lock_read():
931
960
            path = path.rstrip('/')
932
961
            if self.is_versioned(path.rstrip('/')):
933
 
                return self._fileid_map.lookup_file_id(osutils.safe_unicode(path))
 
962
                return self._fileid_map.lookup_file_id(
 
963
                    osutils.safe_unicode(path))
934
964
            return None
935
965
 
936
966
    def has_id(self, file_id):
993
1023
            try:
994
1024
                file, stat_val = self.get_file_with_stat(path)
995
1025
            except (errors.NoSuchFile, IOError):
996
 
                # TODO: Rather than come up with something here, use the old index
 
1026
                # TODO: Rather than come up with something here, use the old
 
1027
                # index
997
1028
                file = BytesIO()
998
1029
                stat_val = os.stat_result(
999
1030
                    (stat.S_IFREG | 0o644, 0, 0, 0, 0, 0, 0, 0, 0, 0))
1000
1031
            with file:
1001
1032
                blob.set_raw_string(file.read())
1002
1033
            # Add object to the repository if it didn't exist yet
1003
 
            if not blob.id in self.store:
 
1034
            if blob.id not in self.store:
1004
1035
                self.store.add_object(blob)
1005
1036
            hexsha = blob.id
1006
1037
        elif kind == "symlink":
1015
1046
            blob.set_raw_string(
1016
1047
                self.get_symlink_target(path).encode("utf-8"))
1017
1048
            # Add object to the repository if it didn't exist yet
1018
 
            if not blob.id in self.store:
 
1049
            if blob.id not in self.store:
1019
1050
                self.store.add_object(blob)
1020
1051
            hexsha = blob.id
1021
1052
        elif kind == "tree-reference":
1022
1053
            if reference_revision is not None:
1023
 
                hexsha = self.branch.lookup_bzr_revision_id(reference_revision)[0]
 
1054
                hexsha = self.branch.lookup_bzr_revision_id(
 
1055
                    reference_revision)[0]
1024
1056
            else:
1025
1057
                hexsha = self._read_submodule_head(path)
1026
1058
                if hexsha is None:
1053
1085
                index = self.index
1054
1086
            for path, value in index.items():
1055
1087
                yield (posixpath.join(basepath, path), value)
1056
 
                (ctime, mtime, dev, ino, mode, uid, gid, size, sha, flags) = value
 
1088
                (ctime, mtime, dev, ino, mode, uid, gid, size, sha,
 
1089
                 flags) = value
1057
1090
                if S_ISGITLINK(mode):
1058
 
                    pass # TODO(jelmer): dive into submodule
1059
 
 
 
1091
                    pass  # TODO(jelmer): dive into submodule
1060
1092
 
1061
1093
    def iter_entries_by_dir(self, specific_files=None, yield_parents=False):
1062
1094
        if yield_parents:
1075
1107
                if self.mapping.is_special_file(path):
1076
1108
                    continue
1077
1109
                path = path.decode("utf-8")
1078
 
                if specific_files is not None and not path in specific_files:
 
1110
                if specific_files is not None and path not in specific_files:
1079
1111
                    continue
1080
1112
                (parent, name) = posixpath.split(path)
1081
1113
                try:
1083
1115
                except errors.NoSuchFile:
1084
1116
                    continue
1085
1117
                if yield_parents or specific_files is None:
1086
 
                    for (dir_path, dir_ie) in self._add_missing_parent_ids(parent,
1087
 
                            dir_ids):
 
1118
                    for (dir_path, dir_ie) in self._add_missing_parent_ids(
 
1119
                            parent, dir_ids):
1088
1120
                        ret[(posixpath.dirname(dir_path), dir_path)] = dir_ie
1089
1121
                file_ie.parent_id = self.path2id(parent)
1090
1122
                ret[(posixpath.dirname(path), path)] = file_ie
1099
1131
    def _get_dir_ie(self, path, parent_id):
1100
1132
        file_id = self.path2id(path)
1101
1133
        return GitTreeDirectory(file_id,
1102
 
            posixpath.basename(path).strip("/"), parent_id)
 
1134
                                posixpath.basename(path).strip("/"), parent_id)
1103
1135
 
1104
1136
    def _get_file_ie(self, name, path, value, parent_id):
1105
1137
        if not isinstance(name, text_type):
1115
1147
        kind = mode_kind(mode)
1116
1148
        ie = entry_factory[kind](file_id, name, parent_id)
1117
1149
        if kind == 'symlink':
1118
 
            ie.symlink_target = self.get_symlink_target(path, file_id)
 
1150
            ie.symlink_target = self.get_symlink_target(path)
1119
1151
        elif kind == 'tree-reference':
1120
 
            ie.reference_revision = self.get_reference_revision(path, file_id)
 
1152
            ie.reference_revision = self.get_reference_revision(path)
1121
1153
        else:
1122
1154
            try:
1123
 
                data = self.get_file_text(path, file_id)
 
1155
                data = self.get_file_text(path)
1124
1156
            except errors.NoSuchFile:
1125
1157
                data = None
1126
1158
            except IOError as e:
1162
1194
            # A directory, perhaps?
1163
1195
            # TODO(jelmer): Deletes that involve submodules?
1164
1196
            for p in list(index):
1165
 
                if p.startswith(subpath+b"/"):
 
1197
                if p.startswith(subpath + b"/"):
1166
1198
                    count += 1
1167
1199
                    self._index_del_entry(index, p)
1168
1200
        else:
1170
1202
        self._versioned_dirs = None
1171
1203
        return count
1172
1204
 
1173
 
    def unversion(self, paths, file_ids=None):
 
1205
    def unversion(self, paths):
1174
1206
        with self.lock_tree_write():
1175
1207
            for path in paths:
1176
1208
                if self._unversion_path(path) == 0:
1185
1217
        # TODO(jelmer): This shouldn't be called, it's inventory specific.
1186
1218
        for (old_path, new_path, file_id, ie) in delta:
1187
1219
            if old_path is not None:
1188
 
                (index, old_subpath) = self._lookup_index(old_path.encode('utf-8'))
 
1220
                (index, old_subpath) = self._lookup_index(
 
1221
                    old_path.encode('utf-8'))
1189
1222
                if old_subpath in index:
1190
1223
                    self._index_del_entry(index, old_subpath)
1191
1224
                    self._versioned_dirs = None
1200
1233
            to_abs = self.abspath(to_dir)
1201
1234
            if not os.path.isdir(to_abs):
1202
1235
                raise errors.BzrMoveFailedError('', to_dir,
1203
 
                    errors.NotADirectory(to_abs))
 
1236
                                                errors.NotADirectory(to_abs))
1204
1237
 
1205
1238
            for from_rel in from_paths:
1206
1239
                from_tail = os.path.split(from_rel)[-1]
1225
1258
                    not self.is_versioned(to_rel))
1226
1259
            if after:
1227
1260
                if not self.has_filename(to_rel):
1228
 
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
1229
 
                        errors.NoSuchFile(to_rel))
 
1261
                    raise errors.BzrMoveFailedError(
 
1262
                        from_rel, to_rel, errors.NoSuchFile(to_rel))
1230
1263
                if self.basis_tree().is_versioned(to_rel):
1231
 
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
1232
 
                        errors.AlreadyVersionedError(to_rel))
 
1264
                    raise errors.BzrMoveFailedError(
 
1265
                        from_rel, to_rel, errors.AlreadyVersionedError(to_rel))
1233
1266
 
1234
1267
                kind = self.kind(to_rel)
1235
1268
            else:
1242
1275
                    exc_type = errors.BzrMoveFailedError
1243
1276
                if self.is_versioned(to_rel):
1244
1277
                    raise exc_type(from_rel, to_rel,
1245
 
                        errors.AlreadyVersionedError(to_rel))
 
1278
                                   errors.AlreadyVersionedError(to_rel))
1246
1279
                if not self.has_filename(from_rel):
1247
 
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
1248
 
                        errors.NoSuchFile(from_rel))
 
1280
                    raise errors.BzrMoveFailedError(
 
1281
                        from_rel, to_rel, errors.NoSuchFile(from_rel))
1249
1282
                kind = self.kind(from_rel)
1250
1283
                if not self.is_versioned(from_rel) and kind != 'directory':
1251
1284
                    raise exc_type(from_rel, to_rel,
1252
 
                        errors.NotVersionedError(from_rel))
 
1285
                                   errors.NotVersionedError(from_rel))
1253
1286
                if self.has_filename(to_rel):
1254
1287
                    raise errors.RenameFailedFilesExist(
1255
1288
                        from_rel, to_rel, errors.FileExists(to_rel))
1260
1293
                (index, from_subpath) = self._lookup_index(from_path)
1261
1294
                if from_subpath not in index:
1262
1295
                    # It's not a file
1263
 
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
 
1296
                    raise errors.BzrMoveFailedError(
 
1297
                        from_rel, to_rel,
1264
1298
                        errors.NotVersionedError(path=from_rel))
1265
1299
 
1266
1300
            if not after:
1268
1302
                    self._rename_one(from_rel, to_rel)
1269
1303
                except OSError as e:
1270
1304
                    if e.errno == errno.ENOENT:
1271
 
                        raise errors.BzrMoveFailedError(from_rel, to_rel,
1272
 
                            errors.NoSuchFile(to_rel))
 
1305
                        raise errors.BzrMoveFailedError(
 
1306
                            from_rel, to_rel, errors.NoSuchFile(to_rel))
1273
1307
                    raise
1274
1308
            if kind != 'directory':
1275
1309
                (index, from_index_path) = self._lookup_index(from_path)
1279
1313
                    pass
1280
1314
                self._index_add_entry(to_rel, kind)
1281
1315
            else:
1282
 
                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'/')]
1283
1318
                for child_path, child_value in todo:
1284
1319
                    (child_to_index, child_to_index_path) = self._lookup_index(
1285
 
                            posixpath.join(to_path, posixpath.relpath(child_path, from_path)))
 
1320
                        posixpath.join(to_path, posixpath.relpath(child_path, from_path)))
1286
1321
                    child_to_index[child_to_index_path] = child_value
1287
1322
                    # TODO(jelmer): Mark individual index as dirty
1288
1323
                    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)
 
1324
                    (child_from_index, child_from_index_path) = self._lookup_index(
 
1325
                        child_path)
 
1326
                    self._index_del_entry(
 
1327
                        child_from_index, child_from_index_path)
1291
1328
 
1292
1329
            self._versioned_dirs = None
1293
1330
            self.flush()
1294
1331
 
1295
1332
    def find_related_paths_across_trees(self, paths, trees=[],
1296
 
            require_versioned=True):
 
1333
                                        require_versioned=True):
1297
1334
        if paths is None:
1298
1335
            return None
1299
1336
 
1336
1373
        else:
1337
1374
            return (kind, None, None, None)
1338
1375
 
1339
 
    def kind(self, relpath, file_id=None):
 
1376
    def kind(self, relpath):
1340
1377
        kind = osutils.file_kind(self.abspath(relpath))
1341
1378
        if kind == 'directory':
1342
1379
            (index, index_path) = self._lookup_index(relpath.encode('utf-8'))
1370
1407
                isinstance(target, MutableGitIndexTree))
1371
1408
 
1372
1409
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
1373
 
            require_versioned=False, extra_trees=None,
1374
 
            want_unversioned=False):
 
1410
                          require_versioned=False, extra_trees=None,
 
1411
                          want_unversioned=False):
1375
1412
        trees = [self.source]
1376
1413
        if extra_trees is not None:
1377
1414
            trees.extend(extra_trees)
1378
1415
        if specific_files is not None:
1379
1416
            specific_files = self.target.find_related_paths_across_trees(
1380
 
                    specific_files, trees,
1381
 
                    require_versioned=require_versioned)
 
1417
                specific_files, trees,
 
1418
                require_versioned=require_versioned)
1382
1419
        # TODO(jelmer): Restrict to specific_files, for performance reasons.
1383
1420
        with self.lock_read():
1384
1421
            return changes_between_git_tree_and_working_copy(
1391
1428
 
1392
1429
 
1393
1430
def changes_between_git_tree_and_working_copy(store, from_tree_sha, target,
1394
 
        want_unchanged=False, want_unversioned=False):
 
1431
                                              want_unchanged=False,
 
1432
                                              want_unversioned=False):
1395
1433
    """Determine the changes between a git tree and a working tree with index.
1396
1434
 
1397
1435
    """
1408
1446
                # Entry was removed; keep it listed, but mark it as gone.
1409
1447
                blobs[path] = (ZERO_SHA, 0)
1410
1448
            elif e.errno == errno.EISDIR:
1411
 
                # Entry was turned into a directory
1412
 
                dirified.append((path, Tree().id, stat.S_IFDIR))
1413
 
                store.add_object(Tree())
 
1449
                # TODO(jelmer): Only do this if 'path' appears in .gitmodules?
 
1450
                if S_ISGITLINK(index_entry.mode):
 
1451
                    blobs[path] = (index_entry.sha, index_entry.mode)
 
1452
                else:
 
1453
                    # Entry was turned into a directory
 
1454
                    dirified.append((path, Tree().id, stat.S_IFDIR))
 
1455
                    store.add_object(Tree())
1414
1456
            else:
1415
1457
                raise
1416
1458
        else:
1426
1468
            if stat.S_ISDIR(st.st_mode):
1427
1469
                blob = Tree()
1428
1470
            else:
1429
 
                blob = blob_from_path_and_stat(target.abspath(e).encode(osutils._fs_enc), st)
 
1471
                blob = blob_from_path_and_stat(
 
1472
                    target.abspath(e).encode(osutils._fs_enc), st)
1430
1473
            store.add_object(blob)
1431
1474
            np = np.encode('utf-8')
1432
1475
            blobs[np] = (blob.id, cleanup_mode(st.st_mode))
1433
1476
            extras.add(np)
1434
 
    to_tree_sha = commit_tree(store, dirified + [(p, s, m) for (p, (s, m)) in blobs.items()])
 
1477
    to_tree_sha = commit_tree(
 
1478
        store, dirified + [(p, s, m) for (p, (s, m)) in blobs.items()])
1435
1479
    return store.tree_changes(
1436
1480
        from_tree_sha, to_tree_sha, include_trees=True,
1437
1481
        want_unchanged=want_unchanged, change_type_same=True), extras