/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 branch.py

Refactor repository initialization.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
"""An adapter between a Git Branch and a Bazaar Branch"""
19
19
 
 
20
from collections import defaultdict
 
21
 
20
22
from dulwich.objects import (
21
23
    Commit,
22
24
    Tag,
23
25
    )
 
26
from dulwich.protocol import ZERO_SHA
24
27
 
25
28
from bzrlib import (
26
29
    branch,
27
30
    bzrdir,
28
31
    config,
29
32
    errors,
30
 
    repository,
 
33
    repository as _mod_repository,
31
34
    revision,
32
35
    tag,
33
36
    transport,
51
54
    NoSuchRef,
52
55
    )
53
56
from bzrlib.plugins.git.refs import (
 
57
    branch_name_to_ref,
 
58
    extract_tags,
 
59
    is_tag,
54
60
    ref_to_branch_name,
55
 
    extract_tags,
 
61
    ref_to_tag_name,
56
62
    tag_name_to_ref,
 
63
    UnpeelMap,
57
64
    )
58
65
 
59
66
from bzrlib.foreign import ForeignBranch
76
83
        return self._lookup_revno(self.new_revid)
77
84
 
78
85
 
79
 
class LocalGitTagDict(tag.BasicTags):
80
 
    """Dictionary with tags in a local repository."""
 
86
class GitTags(tag.BasicTags):
 
87
    """Ref-based tag dictionary."""
81
88
 
82
89
    def __init__(self, branch):
83
90
        self.branch = branch
84
91
        self.repository = branch.repository
85
92
 
 
93
    def get_refs(self):
 
94
        raise NotImplementedError(self.get_refs)
 
95
 
 
96
    def _iter_tag_refs(self, refs):
 
97
        raise NotImplementedError(self._iter_tag_refs)
 
98
 
 
99
    def _merge_to_git(self, to_tags, refs, overwrite=False):
 
100
        target_repo = to_tags.repository
 
101
        conflicts = []
 
102
        for k, v in refs.iteritems():
 
103
            if not is_tag(k):
 
104
                continue
 
105
            if overwrite or not k in self.target.repository.refs:
 
106
                target_repo.refs[k] = v
 
107
            elif target_repo.repository.refs[k] == v:
 
108
                pass
 
109
            else:
 
110
                conflicts.append((ref_to_tag_name(k), v, target_repo.refs[k]))
 
111
        return conflicts
 
112
 
 
113
    def _merge_to_non_git(self, to_tags, refs, overwrite=False):
 
114
        unpeeled_map = defaultdict(set)
 
115
        conflicts = []
 
116
        result = dict(to_tags.get_tag_dict())
 
117
        for n, peeled, unpeeled, bzr_revid in self._iter_tag_refs(refs):
 
118
            if unpeeled is not None:
 
119
                unpeeled_map[peeled].add(unpeeled)
 
120
            if n not in result or overwrite:
 
121
                result[n] = bzr_revid
 
122
            elif result[n] == bzr_revid:
 
123
                pass
 
124
            else:
 
125
                conflicts.append((n, result[n], bzr_revid))
 
126
        to_tags._set_tag_dict(result)
 
127
        if len(unpeeled_map) > 0:
 
128
            map_file = UnpeelMap.from_repository(to_tags.branch.repository)
 
129
            map_file.update(unpeeled_map)
 
130
            map_file.save_in_repository(to_tags.branch.repository)
 
131
        return conflicts
 
132
 
 
133
    def merge_to(self, to_tags, overwrite=False, ignore_master=False,
 
134
                 source_refs=None):
 
135
        """See Tags.merge_to."""
 
136
        if source_refs is None:
 
137
            source_refs = self.get_refs()
 
138
        if self == to_tags:
 
139
            return
 
140
        if isinstance(to_tags, GitTags):
 
141
            return self._merge_to_git(to_tags, source_refs,
 
142
                                      overwrite=overwrite)
 
143
        else:
 
144
            if ignore_master:
 
145
                master = None
 
146
            else:
 
147
                master = to_tags.branch.get_master_branch()
 
148
            conflicts = self._merge_to_non_git(to_tags, source_refs,
 
149
                                              overwrite=overwrite)
 
150
            if master is not None:
 
151
                conflicts += self.merge_to(master.tags, overwrite=overwrite,
 
152
                                           source_refs=source_refs,
 
153
                                           ignore_master=ignore_master)
 
154
            return conflicts
 
155
 
86
156
    def get_tag_dict(self):
87
157
        ret = {}
88
 
        for k,v in extract_tags(self.repository._git.get_refs()).iteritems():
 
158
        refs = self.get_refs()
 
159
        for (name, peeled, unpeeled, bzr_revid) in self._iter_tag_refs(refs):
 
160
            ret[name] = bzr_revid
 
161
        return ret
 
162
 
 
163
 
 
164
class LocalGitTagDict(GitTags):
 
165
    """Dictionary with tags in a local repository."""
 
166
 
 
167
    def __init__(self, branch):
 
168
        super(LocalGitTagDict, self).__init__(branch)
 
169
        self.refs = self.repository._git.refs
 
170
 
 
171
    def get_refs(self):
 
172
        return self.repository._git.get_refs()
 
173
 
 
174
    def _iter_tag_refs(self, refs):
 
175
        """Iterate over the tag refs.
 
176
 
 
177
        :param refs: Refs dictionary (name -> git sha1)
 
178
        :return: iterator over (name, peeled_sha1, unpeeled_sha1, bzr_revid)
 
179
        """
 
180
        for k, (peeled, unpeeled) in extract_tags(refs).iteritems():
89
181
            try:
90
 
                obj = self.repository._git[v]
 
182
                obj = self.repository._git[peeled]
91
183
            except KeyError:
92
 
                mutter("Tag %s points at unknown object %s, ignoring", v, obj)
 
184
                mutter("Tag %s points at unknown object %s, ignoring", peeled,
 
185
                       obj)
93
186
                continue
 
187
            # FIXME: this shouldn't really be necessary, the repository
 
188
            # already should have these unpeeled.
94
189
            while isinstance(obj, Tag):
95
 
                v = obj.object[1]
96
 
                obj = self.repository._git[v]
 
190
                peeled = obj.object[1]
 
191
                obj = self.repository._git[peeled]
97
192
            if not isinstance(obj, Commit):
98
193
                mutter("Tag %s points at object %r that is not a commit, "
99
194
                       "ignoring", k, obj)
100
195
                continue
101
 
            ret[k] = self.branch.lookup_foreign_revision_id(v)
102
 
        return ret
 
196
            yield (k, peeled, unpeeled,
 
197
                   self.branch.lookup_foreign_revision_id(peeled))
103
198
 
104
199
    def _set_tag_dict(self, to_dict):
105
 
        extra = set(self.repository._git.get_refs().keys())
 
200
        extra = set(self.get_refs().keys())
106
201
        for k, revid in to_dict.iteritems():
107
202
            name = tag_name_to_ref(k)
108
203
            if name in extra:
109
204
                extra.remove(name)
110
205
            self.set_tag(k, revid)
111
206
        for name in extra:
112
 
            if name.startswith("refs/tags/"):
 
207
            if is_tag(name):
113
208
                del self.repository._git[name]
114
209
 
115
210
    def set_tag(self, name, revid):
116
 
        self.repository._git.refs[tag_name_to_ref(name)], _ = \
 
211
        self.refs[tag_name_to_ref(name)], _ = \
117
212
            self.branch.lookup_bzr_revision_id(revid)
118
213
 
119
214
 
120
 
class DictTagDict(LocalGitTagDict):
 
215
class DictTagDict(tag.BasicTags):
121
216
 
122
217
    def __init__(self, branch, tags):
123
218
        super(DictTagDict, self).__init__(branch)
129
224
 
130
225
class GitBranchFormat(branch.BranchFormat):
131
226
 
 
227
    @property
 
228
    def _matchingbzrdir(self):
 
229
        from bzrlib.plugins.git.dir import LocalGitControlDirFormat
 
230
        return LocalGitControlDirFormat
 
231
 
132
232
    def get_format_description(self):
133
233
        return 'Git Branch'
134
234
 
138
238
    def supports_tags(self):
139
239
        return True
140
240
 
 
241
    def supports_leaving_lock(self):
 
242
        return False
 
243
 
 
244
    @property
 
245
    def _matchingbzrdir(self):
 
246
        from bzrlib.plugins.git import LocalGitControlDirFormat
 
247
        return LocalGitControlDirFormat()
 
248
 
141
249
    def get_foreign_tests_branch_factory(self):
142
250
        from bzrlib.plugins.git.tests.test_branch import ForeignTestsBranchFactory
143
251
        return ForeignTestsBranchFactory()
149
257
        else:
150
258
            return LocalGitTagDict(branch)
151
259
 
 
260
    def initialize(self, a_bzrdir, name=None, repository=None):
 
261
        from bzrlib.plugins.git.dir import LocalGitDir
 
262
        if not isinstance(a_bzrdir, LocalGitDir):
 
263
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
 
264
        if repository is None:
 
265
            repository = a_bzrdir.open_repository()
 
266
        ref = branch_name_to_ref(name, "HEAD")
 
267
        repository._git[ref] = ZERO_SHA
 
268
        return LocalGitBranch(a_bzrdir, repository, ref, a_bzrdir._lockfiles)
 
269
 
152
270
 
153
271
class GitReadLock(object):
154
272
 
208
326
            self.ref or "HEAD")
209
327
 
210
328
    def generate_revision_history(self, revid, old_revid=None):
211
 
        # FIXME: Check that old_revid is in the ancestry of revid
212
 
        newhead, self.mapping = self.mapping.revision_id_bzr_to_foreign(revid)
 
329
        if revid == NULL_REVISION:
 
330
            newhead = ZERO_SHA
 
331
        else:
 
332
            # FIXME: Check that old_revid is in the ancestry of revid
 
333
            newhead, self.mapping = self.mapping.revision_id_bzr_to_foreign(revid)
213
334
        self._set_head(newhead)
214
335
 
215
336
    def lock_write(self):
265
386
class LocalGitBranch(GitBranch):
266
387
    """A local Git branch."""
267
388
 
268
 
    def __init__(self, bzrdir, repository, name, lockfiles, tagsdict=None):
269
 
        super(LocalGitBranch, self).__init__(bzrdir, repository, name,
 
389
    def __init__(self, bzrdir, repository, ref, lockfiles, tagsdict=None):
 
390
        super(LocalGitBranch, self).__init__(bzrdir, repository, ref,
270
391
              lockfiles, tagsdict)
271
392
        refs = repository._git.get_refs()
272
 
        if not (name in refs.keys() or "HEAD" in refs.keys()):
 
393
        if not (ref in refs.keys() or "HEAD" in refs.keys()):
273
394
            raise errors.NotBranchError(self.base)
274
395
 
275
396
    def create_checkout(self, to_location, revision_id=None, lightweight=False,
351
472
        return True
352
473
 
353
474
 
 
475
def _quick_lookup_revno(local_branch, remote_branch, revid):
 
476
    assert isinstance(revid, str), "was %r" % revid
 
477
    # Try in source branch first, it'll be faster
 
478
    try:
 
479
        return local_branch.revision_id_to_revno(revid)
 
480
    except errors.NoSuchRevision:
 
481
        graph = local_branch.repository.get_graph()
 
482
        try:
 
483
            return graph.find_distance_to_null(revid)
 
484
        except errors.GhostRevisionsHaveNoRevno:
 
485
            # FIXME: Check using graph.find_distance_to_null() ?
 
486
            return remote_branch.revision_id_to_revno(revid)
 
487
 
 
488
 
354
489
class GitBranchPullResult(branch.PullResult):
355
490
 
356
491
    def __init__(self):
371
506
        self._show_tag_conficts(to_file)
372
507
 
373
508
    def _lookup_revno(self, revid):
374
 
        assert isinstance(revid, str), "was %r" % revid
375
 
        # Try in source branch first, it'll be faster
376
 
        try:
377
 
            return self.source_branch.revision_id_to_revno(revid)
378
 
        except errors.NoSuchRevision:
379
 
            # FIXME: Check using graph.find_distance_to_null() ?
380
 
            return self.target_branch.revision_id_to_revno(revid)
 
509
        return _quick_lookup_revno(self.target_branch, self.source_branch, revid)
381
510
 
382
511
    def _get_old_revno(self):
383
512
        if self._old_revno is not None:
403
532
class GitBranchPushResult(branch.BranchPushResult):
404
533
 
405
534
    def _lookup_revno(self, revid):
406
 
        assert isinstance(revid, str), "was %r" % revid
407
 
        # Try in source branch first, it'll be faster
408
 
        try:
409
 
            return self.source_branch.revision_id_to_revno(revid)
410
 
        except errors.NoSuchRevision:
411
 
            # FIXME: Check using graph.find_distance_to_null() ?
412
 
            return self.target_branch.revision_id_to_revno(revid)
 
535
        return _quick_lookup_revno(self.source_branch, self.target_branch, revid)
413
536
 
414
537
    @property
415
538
    def old_revno(self):
417
540
 
418
541
    @property
419
542
    def new_revno(self):
 
543
        new_original_revno = getattr(self, "new_original_revno", None)
 
544
        if new_original_revno:
 
545
            return new_original_revno
 
546
        if getattr(self, "new_original_revid", None) is not None:
 
547
            return self._lookup_revno(self.new_original_revid)
420
548
        return self._lookup_revno(self.new_revid)
421
549
 
422
550
 
425
553
 
426
554
    @staticmethod
427
555
    def _get_branch_formats_to_test():
428
 
        return []
 
556
        try:
 
557
            default_format = branch.format_registry.get_default()
 
558
        except AttributeError:
 
559
            default_format = branch.BranchFormat._default_format
 
560
        return [
 
561
            (GitBranchFormat(), GitBranchFormat()),
 
562
            (GitBranchFormat(), default_format)]
429
563
 
430
564
    @classmethod
431
565
    def _get_interrepo(self, source, target):
432
 
        return repository.InterRepository.get(source.repository,
433
 
            target.repository)
 
566
        return _mod_repository.InterRepository.get(source.repository, target.repository)
434
567
 
435
568
    @classmethod
436
569
    def is_compatible(cls, source, target):
444
577
 
445
578
        Compared to the `update_revisions()` below, this function takes a
446
579
        `limit` argument that limits how many git commits will be converted
447
 
        and returns the new git head.
 
580
        and returns the new git head and remote refs.
448
581
        """
449
582
        interrepo = self._get_interrepo(self.source, self.target)
450
583
        def determine_wants(heads):
476
609
            prev_last_revid = self.target.last_revision()
477
610
        self.target.generate_revision_history(self._last_revid,
478
611
            prev_last_revid)
479
 
        return head
 
612
        return head, refs
480
613
 
481
614
    def update_revisions(self, stop_revision=None, overwrite=False,
482
615
                         graph=None):
514
647
            graph = self.target.repository.get_graph(self.source.repository)
515
648
            (result.old_revno, result.old_revid) = \
516
649
                self.target.last_revision_info()
517
 
            result.new_git_head = self._update_revisions(
 
650
            result.new_git_head, remote_refs = self._update_revisions(
518
651
                stop_revision, overwrite=overwrite, graph=graph, limit=limit)
519
652
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
520
653
                overwrite)
539
672
        result.target_branch = self.target
540
673
        graph = self.target.repository.get_graph(self.source.repository)
541
674
        result.old_revno, result.old_revid = self.target.last_revision_info()
542
 
        result.new_git_head = self._update_revisions(
 
675
        result.new_git_head, remote_refs = self._update_revisions(
543
676
            stop_revision, overwrite=overwrite, graph=graph)
544
677
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
545
678
            overwrite)
556
689
 
557
690
    @staticmethod
558
691
    def _get_branch_formats_to_test():
 
692
        # FIXME
559
693
        return []
560
694
 
561
695
    @classmethod
565
699
                isinstance(target, RemoteGitBranch))
566
700
 
567
701
    def _basic_push(self, overwrite=False, stop_revision=None):
568
 
        from dulwich.protocol import ZERO_SHA
569
702
        result = GitBranchPushResult()
570
703
        result.source_branch = self.source
571
704
        result.target_branch = self.target
589
722
 
590
723
    @staticmethod
591
724
    def _get_branch_formats_to_test():
 
725
        # FIXME
592
726
        return []
593
727
 
594
728
    @classmethod
604
738
        result.old_revid = self.target.last_revision()
605
739
        refs, stop_revision = self.update_refs(stop_revision)
606
740
        self.target.generate_revision_history(stop_revision, result.old_revid)
607
 
        self.update_tags(refs)
 
741
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
742
            source_refs=refs, overwrite=overwrite)
608
743
        result.new_revid = self.target.last_revision()
609
744
        return result
610
745
 
611
 
    def update_tags(self, refs):
612
 
        for name, v in extract_tags(refs).iteritems():
613
 
            revid = self.target.lookup_foreign_revision_id(v)
614
 
            self.target.tags.set_tag(name, revid)
615
 
 
616
746
    def update_refs(self, stop_revision=None):
617
 
        interrepo = repository.InterRepository.get(self.source.repository,
 
747
        interrepo = _mod_repository.InterRepository.get(self.source.repository,
618
748
            self.target.repository)
619
749
        if stop_revision is None:
620
750
            refs = interrepo.fetch(branches=["HEAD"])
634
764
        result.old_revid = self.target.last_revision()
635
765
        refs, stop_revision = self.update_refs(stop_revision)
636
766
        self.target.generate_revision_history(stop_revision, result.old_revid)
637
 
        self.update_tags(refs)
 
767
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
768
            overwrite=overwrite, source_refs=refs)
638
769
        result.new_revid = self.target.last_revision()
639
770
        return result
640
771
 
644
775
 
645
776
    def __init__(self, source, target):
646
777
        super(InterToGitBranch, self).__init__(source, target)
647
 
        self.interrepo = repository.InterRepository.get(source.repository,
 
778
        self.interrepo = _mod_repository.InterRepository.get(source.repository,
648
779
                                           target.repository)
649
780
 
650
781
    @staticmethod
651
782
    def _get_branch_formats_to_test():
652
 
        return []
 
783
        try:
 
784
            default_format = branch.format_registry.get_default()
 
785
        except AttributeError:
 
786
            default_format = branch.BranchFormat._default_format
 
787
        return [(default_format, GitBranchFormat())]
653
788
 
654
789
    @classmethod
655
790
    def is_compatible(self, source, target):
661
796
 
662
797
    def _get_new_refs(self, stop_revision=None):
663
798
        if stop_revision is None:
664
 
            stop_revision = self.source.last_revision()
 
799
            (stop_revno, stop_revision) = self.source.last_revision_info()
665
800
        assert type(stop_revision) is str
666
801
        main_ref = self.target.ref or "refs/heads/master"
667
802
        refs = { main_ref: (None, stop_revision) }
668
803
        for name, revid in self.source.tags.get_tag_dict().iteritems():
669
804
            if self.source.repository.has_revision(revid):
670
805
                refs[tag_name_to_ref(name)] = (None, revid)
671
 
        return refs, main_ref
 
806
        return refs, main_ref, (stop_revno, stop_revision)
672
807
 
673
808
    def pull(self, overwrite=False, stop_revision=None, local=False,
674
809
             possible_transports=None):
675
 
        from dulwich.protocol import ZERO_SHA
676
810
        result = GitBranchPullResult()
677
811
        result.source_branch = self.source
678
812
        result.target_branch = self.target
679
 
        new_refs, main_ref = self._get_new_refs(stop_revision)
 
813
        new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision)
680
814
        def update_refs(old_refs):
681
815
            refs = dict(old_refs)
682
816
            # FIXME: Check for diverged branches
683
817
            refs.update(new_refs)
684
818
            return refs
685
819
        old_refs, new_refs = self.interrepo.fetch_refs(update_refs)
686
 
        result.old_revid = self.target.lookup_foreign_revision_id(
687
 
            old_refs.get(main_ref, ZERO_SHA))
688
 
        result.new_revid = new_refs[main_ref]
 
820
        (result.old_revid, old_sha1) = old_refs.get(main_ref, (ZERO_SHA, NULL_REVISION))
 
821
        if result.old_revid is None:
 
822
            result.old_revid = self.target.lookup_foreign_revision_id(old_sha1)
 
823
        result.new_revid = new_refs[main_ref][1]
689
824
        return result
690
825
 
691
826
    def push(self, overwrite=False, stop_revision=None,
692
827
             _override_hook_source_branch=None):
693
 
        from dulwich.protocol import ZERO_SHA
694
828
        result = GitBranchPushResult()
695
829
        result.source_branch = self.source
696
830
        result.target_branch = self.target
697
 
        new_refs, main_ref = self._get_new_refs(stop_revision)
 
831
        new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision)
698
832
        def update_refs(old_refs):
699
833
            refs = dict(old_refs)
700
834
            # FIXME: Check for diverged branches
704
838
        (result.old_revid, old_sha1) = old_refs.get(main_ref, (ZERO_SHA, NULL_REVISION))
705
839
        if result.old_revid is None:
706
840
            result.old_revid = self.target.lookup_foreign_revision_id(old_sha1)
707
 
        result.new_revid = new_refs[main_ref]
 
841
        result.new_revid = new_refs[main_ref][1]
708
842
        return result
709
843
 
710
844
    def lossy_push(self, stop_revision=None):
711
845
        result = GitBranchPushResult()
712
846
        result.source_branch = self.source
713
847
        result.target_branch = self.target
714
 
        new_refs, main_ref = self._get_new_refs(stop_revision)
 
848
        new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision)
715
849
        def update_refs(old_refs):
716
850
            refs = dict(old_refs)
717
851
            # FIXME: Check for diverged branches
721
855
            update_refs)
722
856
        result.old_revid = old_refs.get(self.target.ref, (None, NULL_REVISION))[1]
723
857
        result.new_revid = new_refs[main_ref][1]
 
858
        (result.new_original_revno, result.new_original_revid) = stop_revinfo
724
859
        return result
725
860
 
726
861