~ubuntu-branches/debian/jessie/bzr-git/jessie

« back to all changes in this revision

Viewing changes to branch.py

  • Committer: Bazaar Package Importer
  • Author(s): Jelmer Vernooij
  • Date: 2011-01-21 22:15:09 UTC
  • mfrom: (1.1.13 upstream) (8.1.7 sid)
  • Revision ID: james.westby@ubuntu.com-20110121221509-f132fmma13dpxcw1
* New upstream release.
* Run testsuite as part of build.
* Mark as only supporting Python >= 2.4.
* Add Enhances: bzr.

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,
51
53
    NoSuchRef,
52
54
    )
53
55
from bzrlib.plugins.git.refs import (
 
56
    extract_tags,
 
57
    is_tag,
54
58
    ref_to_branch_name,
55
 
    extract_tags,
 
59
    ref_to_tag_name,
56
60
    tag_name_to_ref,
 
61
    UnpeelMap,
57
62
    )
58
63
 
59
64
from bzrlib.foreign import ForeignBranch
76
81
        return self._lookup_revno(self.new_revid)
77
82
 
78
83
 
79
 
class LocalGitTagDict(tag.BasicTags):
80
 
    """Dictionary with tags in a local repository."""
 
84
class GitTags(tag.BasicTags):
 
85
    """Ref-based tag dictionary."""
81
86
 
82
87
    def __init__(self, branch):
83
88
        self.branch = branch
84
89
        self.repository = branch.repository
85
90
 
 
91
    def get_refs(self):
 
92
        raise NotImplementedError(self.get_refs)
 
93
 
 
94
    def _iter_tag_refs(self, refs):
 
95
        raise NotImplementedError(self._iter_tag_refs)
 
96
 
 
97
    def _merge_to_git(self, to_tags, refs, overwrite=False):
 
98
        target_repo = to_tags.repository
 
99
        conflicts = []
 
100
        for k, v in refs.iteritems():
 
101
            if not is_tag(k):
 
102
                continue
 
103
            if overwrite or not k in self.target.repository.refs:
 
104
                target_repo.refs[k] = v
 
105
            elif target_repo.repository.refs[k] == v:
 
106
                pass
 
107
            else:
 
108
                conflicts.append((ref_to_tag_name(k), v, target_repo.refs[k]))
 
109
        return conflicts
 
110
 
 
111
    def _merge_to_non_git(self, to_tags, refs, overwrite=False):
 
112
        unpeeled_map = defaultdict(set)
 
113
        conflicts = []
 
114
        result = dict(to_tags.get_tag_dict())
 
115
        for n, peeled, unpeeled, bzr_revid in self._iter_tag_refs(refs):
 
116
            if unpeeled is not None:
 
117
                unpeeled_map[peeled].add(unpeeled)
 
118
            if n not in result or overwrite:
 
119
                result[n] = bzr_revid
 
120
            elif result[n] == bzr_revid:
 
121
                pass
 
122
            else:
 
123
                conflicts.append((n, result[n], bzr_revid))
 
124
        to_tags._set_tag_dict(result)
 
125
        if len(unpeeled_map) > 0:
 
126
            map_file = UnpeelMap.from_repository(to_tags.branch.repository)
 
127
            map_file.update(unpeeled_map)
 
128
            map_file.save_in_repository(to_tags.branch.repository)
 
129
        return conflicts
 
130
 
 
131
    def merge_to(self, to_tags, overwrite=False, ignore_master=False,
 
132
                 source_refs=None):
 
133
        if source_refs is None:
 
134
            source_refs = self.get_refs()
 
135
        if self == to_tags:
 
136
            return
 
137
        if isinstance(to_tags, GitTags):
 
138
            return self._merge_to_git(to_tags, source_refs,
 
139
                                      overwrite=overwrite)
 
140
        else:
 
141
            if ignore_master:
 
142
                master = None
 
143
            else:
 
144
                master = to_tags.branch.get_master_branch()
 
145
            conflicts = self._merge_to_non_git(to_tags, source_refs,
 
146
                                              overwrite=overwrite)
 
147
            if master is not None:
 
148
                conflicts += self.merge_to(to_tags, overwrite=overwrite,
 
149
                                           source_refs=source_refs,
 
150
                                           ignore_master=ignore_master)
 
151
            return conflicts
 
152
 
86
153
    def get_tag_dict(self):
87
154
        ret = {}
88
 
        for k,v in extract_tags(self.repository._git.get_refs()).iteritems():
 
155
        refs = self.get_refs()
 
156
        for (name, peeled, unpeeled, bzr_revid) in self._iter_tag_refs(refs):
 
157
            ret[name] = bzr_revid
 
158
        return ret
 
159
 
 
160
 
 
161
class LocalGitTagDict(GitTags):
 
162
    """Dictionary with tags in a local repository."""
 
163
 
 
164
    def __init__(self, branch):
 
165
        super(LocalGitTagDict, self).__init__(branch)
 
166
        self.refs = self.repository._git.refs
 
167
 
 
168
    def get_refs(self):
 
169
        return self.repository._git.get_refs()
 
170
 
 
171
    def _iter_tag_refs(self, refs):
 
172
        """Iterate over the tag refs.
 
173
 
 
174
        :param refs: Refs dictionary (name -> git sha1)
 
175
        :return: iterator over (name, peeled_sha1, unpeeled_sha1, bzr_revid)
 
176
        """
 
177
        for k, (peeled, unpeeled) in extract_tags(refs).iteritems():
89
178
            try:
90
 
                obj = self.repository._git[v]
 
179
                obj = self.repository._git[peeled]
91
180
            except KeyError:
92
 
                mutter("Tag %s points at unknown object %s, ignoring", v, obj)
 
181
                mutter("Tag %s points at unknown object %s, ignoring", peeled,
 
182
                       obj)
93
183
                continue
 
184
            # FIXME: this shouldn't really be necessary, the repository
 
185
            # already should have these unpeeled.
94
186
            while isinstance(obj, Tag):
95
 
                v = obj.object[1]
96
 
                obj = self.repository._git[v]
 
187
                peeled = obj.object[1]
 
188
                obj = self.repository._git[peeled]
97
189
            if not isinstance(obj, Commit):
98
190
                mutter("Tag %s points at object %r that is not a commit, "
99
191
                       "ignoring", k, obj)
100
192
                continue
101
 
            ret[k] = self.branch.lookup_foreign_revision_id(v)
102
 
        return ret
 
193
            yield (k, peeled, unpeeled,
 
194
                   self.branch.lookup_foreign_revision_id(peeled))
103
195
 
104
196
    def _set_tag_dict(self, to_dict):
105
 
        extra = set(self.repository._git.get_refs().keys())
 
197
        extra = set(self.get_refs().keys())
106
198
        for k, revid in to_dict.iteritems():
107
199
            name = tag_name_to_ref(k)
108
200
            if name in extra:
109
201
                extra.remove(name)
110
202
            self.set_tag(k, revid)
111
203
        for name in extra:
112
 
            if name.startswith("refs/tags/"):
 
204
            if is_tag(name):
113
205
                del self.repository._git[name]
114
206
 
115
207
    def set_tag(self, name, revid):
116
 
        self.repository._git.refs[tag_name_to_ref(name)], _ = \
 
208
        self.refs[tag_name_to_ref(name)], _ = \
117
209
            self.branch.lookup_bzr_revision_id(revid)
118
210
 
119
211
 
351
443
        return True
352
444
 
353
445
 
 
446
def _quick_lookup_revno(local_branch, remote_branch, revid):
 
447
    assert isinstance(revid, str), "was %r" % revid
 
448
    # Try in source branch first, it'll be faster
 
449
    try:
 
450
        return local_branch.revision_id_to_revno(revid)
 
451
    except errors.NoSuchRevision:
 
452
        graph = local_branch.repository.get_graph()
 
453
        try:
 
454
            return graph.find_distance_to_null(revid)
 
455
        except errors.GhostRevisionsHaveNoRevno:
 
456
            # FIXME: Check using graph.find_distance_to_null() ?
 
457
            return remote_branch.revision_id_to_revno(revid)
 
458
 
 
459
 
354
460
class GitBranchPullResult(branch.PullResult):
355
461
 
356
462
    def __init__(self):
371
477
        self._show_tag_conficts(to_file)
372
478
 
373
479
    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)
 
480
        return _quick_lookup_revno(self.target_branch, self.source_branch, revid)
381
481
 
382
482
    def _get_old_revno(self):
383
483
        if self._old_revno is not None:
403
503
class GitBranchPushResult(branch.BranchPushResult):
404
504
 
405
505
    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)
 
506
        return _quick_lookup_revno(self.source_branch, self.target_branch, revid)
413
507
 
414
508
    @property
415
509
    def old_revno(self):
417
511
 
418
512
    @property
419
513
    def new_revno(self):
 
514
        new_original_revno = getattr(self, "new_original_revno", None)
 
515
        if new_original_revno:
 
516
            return new_original_revno
 
517
        if getattr(self, "new_original_revid", None) is not None:
 
518
            return self._lookup_revno(self.new_original_revid)
420
519
        return self._lookup_revno(self.new_revid)
421
520
 
422
521
 
444
543
 
445
544
        Compared to the `update_revisions()` below, this function takes a
446
545
        `limit` argument that limits how many git commits will be converted
447
 
        and returns the new git head.
 
546
        and returns the new git head and remote refs.
448
547
        """
449
548
        interrepo = self._get_interrepo(self.source, self.target)
450
549
        def determine_wants(heads):
476
575
            prev_last_revid = self.target.last_revision()
477
576
        self.target.generate_revision_history(self._last_revid,
478
577
            prev_last_revid)
479
 
        return head
 
578
        return head, refs
480
579
 
481
580
    def update_revisions(self, stop_revision=None, overwrite=False,
482
581
                         graph=None):
514
613
            graph = self.target.repository.get_graph(self.source.repository)
515
614
            (result.old_revno, result.old_revid) = \
516
615
                self.target.last_revision_info()
517
 
            result.new_git_head = self._update_revisions(
 
616
            result.new_git_head, remote_refs = self._update_revisions(
518
617
                stop_revision, overwrite=overwrite, graph=graph, limit=limit)
519
618
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
520
619
                overwrite)
539
638
        result.target_branch = self.target
540
639
        graph = self.target.repository.get_graph(self.source.repository)
541
640
        result.old_revno, result.old_revid = self.target.last_revision_info()
542
 
        result.new_git_head = self._update_revisions(
 
641
        result.new_git_head, remote_refs = self._update_revisions(
543
642
            stop_revision, overwrite=overwrite, graph=graph)
544
643
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
545
644
            overwrite)
604
703
        result.old_revid = self.target.last_revision()
605
704
        refs, stop_revision = self.update_refs(stop_revision)
606
705
        self.target.generate_revision_history(stop_revision, result.old_revid)
607
 
        self.update_tags(refs)
 
706
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
707
            source_refs=refs, overwrite=overwrite)
608
708
        result.new_revid = self.target.last_revision()
609
709
        return result
610
710
 
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
711
    def update_refs(self, stop_revision=None):
617
712
        interrepo = repository.InterRepository.get(self.source.repository,
618
713
            self.target.repository)
634
729
        result.old_revid = self.target.last_revision()
635
730
        refs, stop_revision = self.update_refs(stop_revision)
636
731
        self.target.generate_revision_history(stop_revision, result.old_revid)
637
 
        self.update_tags(refs)
 
732
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
733
            overwrite=overwrite, source_refs=refs)
638
734
        result.new_revid = self.target.last_revision()
639
735
        return result
640
736
 
661
757
 
662
758
    def _get_new_refs(self, stop_revision=None):
663
759
        if stop_revision is None:
664
 
            stop_revision = self.source.last_revision()
 
760
            (stop_revno, stop_revision) = self.source.last_revision_info()
665
761
        assert type(stop_revision) is str
666
762
        main_ref = self.target.ref or "refs/heads/master"
667
763
        refs = { main_ref: (None, stop_revision) }
668
764
        for name, revid in self.source.tags.get_tag_dict().iteritems():
669
765
            if self.source.repository.has_revision(revid):
670
766
                refs[tag_name_to_ref(name)] = (None, revid)
671
 
        return refs, main_ref
 
767
        return refs, main_ref, (stop_revno, stop_revision)
672
768
 
673
769
    def pull(self, overwrite=False, stop_revision=None, local=False,
674
770
             possible_transports=None):
676
772
        result = GitBranchPullResult()
677
773
        result.source_branch = self.source
678
774
        result.target_branch = self.target
679
 
        new_refs, main_ref = self._get_new_refs(stop_revision)
 
775
        new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision)
680
776
        def update_refs(old_refs):
681
777
            refs = dict(old_refs)
682
778
            # FIXME: Check for diverged branches
695
791
        result = GitBranchPushResult()
696
792
        result.source_branch = self.source
697
793
        result.target_branch = self.target
698
 
        new_refs, main_ref = self._get_new_refs(stop_revision)
 
794
        new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision)
699
795
        def update_refs(old_refs):
700
796
            refs = dict(old_refs)
701
797
            # FIXME: Check for diverged branches
712
808
        result = GitBranchPushResult()
713
809
        result.source_branch = self.source
714
810
        result.target_branch = self.target
715
 
        new_refs, main_ref = self._get_new_refs(stop_revision)
 
811
        new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision)
716
812
        def update_refs(old_refs):
717
813
            refs = dict(old_refs)
718
814
            # FIXME: Check for diverged branches
722
818
            update_refs)
723
819
        result.old_revid = old_refs.get(self.target.ref, (None, NULL_REVISION))[1]
724
820
        result.new_revid = new_refs[main_ref][1]
 
821
        (result.new_original_revno, result.new_original_revid) = stop_revinfo
725
822
        return result
726
823
 
727
824