/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

Use transports in git-import.

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 target_repo._git.refs:
 
106
                target_repo._git.refs[k] = v
 
107
            elif target_repo._git.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)
138
233
    def supports_tags(self):
139
234
        return True
140
235
 
 
236
    def supports_leaving_lock(self):
 
237
        return False
 
238
 
 
239
    @property
 
240
    def _matchingbzrdir(self):
 
241
        from bzrlib.plugins.git.dir import LocalGitControlDirFormat
 
242
        return LocalGitControlDirFormat()
 
243
 
141
244
    def get_foreign_tests_branch_factory(self):
142
245
        from bzrlib.plugins.git.tests.test_branch import ForeignTestsBranchFactory
143
246
        return ForeignTestsBranchFactory()
149
252
        else:
150
253
            return LocalGitTagDict(branch)
151
254
 
 
255
    def initialize(self, a_bzrdir, name=None, repository=None):
 
256
        from bzrlib.plugins.git.dir import LocalGitDir
 
257
        if not isinstance(a_bzrdir, LocalGitDir):
 
258
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
 
259
        if repository is None:
 
260
            repository = a_bzrdir.open_repository()
 
261
        ref = branch_name_to_ref(name, "HEAD")
 
262
        repository._git[ref] = ZERO_SHA
 
263
        return LocalGitBranch(a_bzrdir, repository, ref, a_bzrdir._lockfiles)
 
264
 
152
265
 
153
266
class GitReadLock(object):
154
267
 
165
278
class GitBranch(ForeignBranch):
166
279
    """An adapter to git repositories for bzr Branch objects."""
167
280
 
 
281
    @property
 
282
    def control_transport(self):
 
283
        return self.bzrdir.control_transport
 
284
 
168
285
    def __init__(self, bzrdir, repository, ref, lockfiles, tagsdict=None):
169
286
        self.repository = repository
170
287
        self._format = GitBranchFormat()
208
325
            self.ref or "HEAD")
209
326
 
210
327
    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)
 
328
        if revid == NULL_REVISION:
 
329
            newhead = ZERO_SHA
 
330
        else:
 
331
            # FIXME: Check that old_revid is in the ancestry of revid
 
332
            newhead, self.mapping = self.mapping.revision_id_bzr_to_foreign(revid)
213
333
        self._set_head(newhead)
214
334
 
215
335
    def lock_write(self):
265
385
class LocalGitBranch(GitBranch):
266
386
    """A local Git branch."""
267
387
 
268
 
    def __init__(self, bzrdir, repository, name, lockfiles, tagsdict=None):
269
 
        super(LocalGitBranch, self).__init__(bzrdir, repository, name,
 
388
    def __init__(self, bzrdir, repository, ref, lockfiles, tagsdict=None):
 
389
        super(LocalGitBranch, self).__init__(bzrdir, repository, ref,
270
390
              lockfiles, tagsdict)
271
391
        refs = repository._git.get_refs()
272
 
        if not (name in refs.keys() or "HEAD" in refs.keys()):
 
392
        if not (ref in refs.keys() or "HEAD" in refs.keys()):
273
393
            raise errors.NotBranchError(self.base)
274
394
 
275
395
    def create_checkout(self, to_location, revision_id=None, lightweight=False,
351
471
        return True
352
472
 
353
473
 
 
474
def _quick_lookup_revno(local_branch, remote_branch, revid):
 
475
    assert isinstance(revid, str), "was %r" % revid
 
476
    # Try in source branch first, it'll be faster
 
477
    try:
 
478
        return local_branch.revision_id_to_revno(revid)
 
479
    except errors.NoSuchRevision:
 
480
        graph = local_branch.repository.get_graph()
 
481
        try:
 
482
            return graph.find_distance_to_null(revid)
 
483
        except errors.GhostRevisionsHaveNoRevno:
 
484
            # FIXME: Check using graph.find_distance_to_null() ?
 
485
            return remote_branch.revision_id_to_revno(revid)
 
486
 
 
487
 
354
488
class GitBranchPullResult(branch.PullResult):
355
489
 
356
490
    def __init__(self):
371
505
        self._show_tag_conficts(to_file)
372
506
 
373
507
    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)
 
508
        return _quick_lookup_revno(self.target_branch, self.source_branch, revid)
381
509
 
382
510
    def _get_old_revno(self):
383
511
        if self._old_revno is not None:
403
531
class GitBranchPushResult(branch.BranchPushResult):
404
532
 
405
533
    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)
 
534
        return _quick_lookup_revno(self.source_branch, self.target_branch, revid)
413
535
 
414
536
    @property
415
537
    def old_revno(self):
417
539
 
418
540
    @property
419
541
    def new_revno(self):
 
542
        new_original_revno = getattr(self, "new_original_revno", None)
 
543
        if new_original_revno:
 
544
            return new_original_revno
 
545
        if getattr(self, "new_original_revid", None) is not None:
 
546
            return self._lookup_revno(self.new_original_revid)
420
547
        return self._lookup_revno(self.new_revid)
421
548
 
422
549
 
425
552
 
426
553
    @staticmethod
427
554
    def _get_branch_formats_to_test():
428
 
        return []
 
555
        try:
 
556
            default_format = branch.format_registry.get_default()
 
557
        except AttributeError:
 
558
            default_format = branch.BranchFormat._default_format
 
559
        return [
 
560
            (GitBranchFormat(), GitBranchFormat()),
 
561
            (GitBranchFormat(), default_format)]
429
562
 
430
563
    @classmethod
431
564
    def _get_interrepo(self, source, target):
432
 
        return repository.InterRepository.get(source.repository,
433
 
            target.repository)
 
565
        return _mod_repository.InterRepository.get(source.repository, target.repository)
434
566
 
435
567
    @classmethod
436
568
    def is_compatible(cls, source, target):
444
576
 
445
577
        Compared to the `update_revisions()` below, this function takes a
446
578
        `limit` argument that limits how many git commits will be converted
447
 
        and returns the new git head.
 
579
        and returns the new git head and remote refs.
448
580
        """
449
581
        interrepo = self._get_interrepo(self.source, self.target)
450
582
        def determine_wants(heads):
476
608
            prev_last_revid = self.target.last_revision()
477
609
        self.target.generate_revision_history(self._last_revid,
478
610
            prev_last_revid)
479
 
        return head
 
611
        return head, refs
480
612
 
481
613
    def update_revisions(self, stop_revision=None, overwrite=False,
482
614
                         graph=None):
514
646
            graph = self.target.repository.get_graph(self.source.repository)
515
647
            (result.old_revno, result.old_revid) = \
516
648
                self.target.last_revision_info()
517
 
            result.new_git_head = self._update_revisions(
 
649
            result.new_git_head, remote_refs = self._update_revisions(
518
650
                stop_revision, overwrite=overwrite, graph=graph, limit=limit)
519
651
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
520
652
                overwrite)
539
671
        result.target_branch = self.target
540
672
        graph = self.target.repository.get_graph(self.source.repository)
541
673
        result.old_revno, result.old_revid = self.target.last_revision_info()
542
 
        result.new_git_head = self._update_revisions(
 
674
        result.new_git_head, remote_refs = self._update_revisions(
543
675
            stop_revision, overwrite=overwrite, graph=graph)
544
676
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
545
677
            overwrite)
556
688
 
557
689
    @staticmethod
558
690
    def _get_branch_formats_to_test():
 
691
        # FIXME
559
692
        return []
560
693
 
561
694
    @classmethod
565
698
                isinstance(target, RemoteGitBranch))
566
699
 
567
700
    def _basic_push(self, overwrite=False, stop_revision=None):
568
 
        from dulwich.protocol import ZERO_SHA
569
701
        result = GitBranchPushResult()
570
702
        result.source_branch = self.source
571
703
        result.target_branch = self.target
589
721
 
590
722
    @staticmethod
591
723
    def _get_branch_formats_to_test():
 
724
        # FIXME
592
725
        return []
593
726
 
594
727
    @classmethod
604
737
        result.old_revid = self.target.last_revision()
605
738
        refs, stop_revision = self.update_refs(stop_revision)
606
739
        self.target.generate_revision_history(stop_revision, result.old_revid)
607
 
        self.update_tags(refs)
 
740
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
741
            source_refs=refs, overwrite=overwrite)
608
742
        result.new_revid = self.target.last_revision()
609
743
        return result
610
744
 
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
745
    def update_refs(self, stop_revision=None):
617
 
        interrepo = repository.InterRepository.get(self.source.repository,
 
746
        interrepo = _mod_repository.InterRepository.get(self.source.repository,
618
747
            self.target.repository)
619
748
        if stop_revision is None:
620
749
            refs = interrepo.fetch(branches=["HEAD"])
634
763
        result.old_revid = self.target.last_revision()
635
764
        refs, stop_revision = self.update_refs(stop_revision)
636
765
        self.target.generate_revision_history(stop_revision, result.old_revid)
637
 
        self.update_tags(refs)
 
766
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
767
            overwrite=overwrite, source_refs=refs)
638
768
        result.new_revid = self.target.last_revision()
639
769
        return result
640
770
 
644
774
 
645
775
    def __init__(self, source, target):
646
776
        super(InterToGitBranch, self).__init__(source, target)
647
 
        self.interrepo = repository.InterRepository.get(source.repository,
 
777
        self.interrepo = _mod_repository.InterRepository.get(source.repository,
648
778
                                           target.repository)
649
779
 
650
780
    @staticmethod
651
781
    def _get_branch_formats_to_test():
652
 
        return []
 
782
        try:
 
783
            default_format = branch.format_registry.get_default()
 
784
        except AttributeError:
 
785
            default_format = branch.BranchFormat._default_format
 
786
        return [(default_format, GitBranchFormat())]
653
787
 
654
788
    @classmethod
655
789
    def is_compatible(self, source, target):
661
795
 
662
796
    def _get_new_refs(self, stop_revision=None):
663
797
        if stop_revision is None:
664
 
            stop_revision = self.source.last_revision()
 
798
            (stop_revno, stop_revision) = self.source.last_revision_info()
665
799
        assert type(stop_revision) is str
666
800
        main_ref = self.target.ref or "refs/heads/master"
667
801
        refs = { main_ref: (None, stop_revision) }
668
802
        for name, revid in self.source.tags.get_tag_dict().iteritems():
669
803
            if self.source.repository.has_revision(revid):
670
804
                refs[tag_name_to_ref(name)] = (None, revid)
671
 
        return refs, main_ref
 
805
        return refs, main_ref, (stop_revno, stop_revision)
672
806
 
673
807
    def pull(self, overwrite=False, stop_revision=None, local=False,
674
 
             possible_transports=None):
675
 
        from dulwich.protocol import ZERO_SHA
 
808
             possible_transports=None, run_hooks=True):
676
809
        result = GitBranchPullResult()
677
810
        result.source_branch = self.source
678
811
        result.target_branch = self.target
679
 
        new_refs, main_ref = self._get_new_refs(stop_revision)
 
812
        new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision)
680
813
        def update_refs(old_refs):
681
814
            refs = dict(old_refs)
682
815
            # FIXME: Check for diverged branches
683
816
            refs.update(new_refs)
684
817
            return refs
685
818
        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]
 
819
        (result.old_revid, old_sha1) = old_refs.get(main_ref, (ZERO_SHA, NULL_REVISION))
 
820
        if result.old_revid is None:
 
821
            result.old_revid = self.target.lookup_foreign_revision_id(old_sha1)
 
822
        result.new_revid = new_refs[main_ref][1]
689
823
        return result
690
824
 
691
825
    def push(self, overwrite=False, stop_revision=None,
692
826
             _override_hook_source_branch=None):
693
 
        from dulwich.protocol import ZERO_SHA
694
827
        result = GitBranchPushResult()
695
828
        result.source_branch = self.source
696
829
        result.target_branch = self.target
697
 
        new_refs, main_ref = self._get_new_refs(stop_revision)
 
830
        new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision)
698
831
        def update_refs(old_refs):
699
832
            refs = dict(old_refs)
700
833
            # FIXME: Check for diverged branches
704
837
        (result.old_revid, old_sha1) = old_refs.get(main_ref, (ZERO_SHA, NULL_REVISION))
705
838
        if result.old_revid is None:
706
839
            result.old_revid = self.target.lookup_foreign_revision_id(old_sha1)
707
 
        result.new_revid = new_refs[main_ref]
 
840
        result.new_revid = new_refs[main_ref][1]
708
841
        return result
709
842
 
710
843
    def lossy_push(self, stop_revision=None):
711
844
        result = GitBranchPushResult()
712
845
        result.source_branch = self.source
713
846
        result.target_branch = self.target
714
 
        new_refs, main_ref = self._get_new_refs(stop_revision)
 
847
        new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision)
715
848
        def update_refs(old_refs):
716
849
            refs = dict(old_refs)
717
850
            # FIXME: Check for diverged branches
721
854
            update_refs)
722
855
        result.old_revid = old_refs.get(self.target.ref, (None, NULL_REVISION))[1]
723
856
        result.new_revid = new_refs[main_ref][1]
 
857
        (result.new_original_revno, result.new_original_revid) = stop_revinfo
724
858
        return result
725
859
 
726
860