/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

More work on roundtrip push support.

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
 
 
22
20
from dulwich.objects import (
23
21
    Commit,
24
22
    Tag,
25
 
    ZERO_SHA,
26
23
    )
27
24
 
28
25
from bzrlib import (
30
27
    bzrdir,
31
28
    config,
32
29
    errors,
33
 
    repository as _mod_repository,
 
30
    repository,
34
31
    revision,
35
32
    tag,
36
33
    transport,
54
51
    NoSuchRef,
55
52
    )
56
53
from bzrlib.plugins.git.refs import (
57
 
    branch_name_to_ref,
 
54
    ref_to_branch_name,
58
55
    extract_tags,
59
 
    is_tag,
60
 
    ref_to_branch_name,
61
 
    ref_to_tag_name,
62
56
    tag_name_to_ref,
63
 
    UnpeelMap,
64
57
    )
65
58
 
66
59
from bzrlib.foreign import ForeignBranch
83
76
        return self._lookup_revno(self.new_revid)
84
77
 
85
78
 
86
 
class GitTags(tag.BasicTags):
87
 
    """Ref-based tag dictionary."""
 
79
class LocalGitTagDict(tag.BasicTags):
 
80
    """Dictionary with tags in a local repository."""
88
81
 
89
82
    def __init__(self, branch):
90
83
        self.branch = branch
91
84
        self.repository = branch.repository
92
85
 
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
 
 
156
86
    def get_tag_dict(self):
157
87
        ret = {}
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():
 
88
        for k,v in extract_tags(self.repository._git.get_refs()).iteritems():
181
89
            try:
182
 
                obj = self.repository._git[peeled]
 
90
                obj = self.repository._git[v]
183
91
            except KeyError:
184
 
                mutter("Tag %s points at unknown object %s, ignoring", peeled,
185
 
                       obj)
 
92
                mutter("Tag %s points at unknown object %s, ignoring", v, obj)
186
93
                continue
187
 
            # FIXME: this shouldn't really be necessary, the repository
188
 
            # already should have these unpeeled.
189
94
            while isinstance(obj, Tag):
190
 
                peeled = obj.object[1]
191
 
                obj = self.repository._git[peeled]
 
95
                v = obj.object[1]
 
96
                obj = self.repository._git[v]
192
97
            if not isinstance(obj, Commit):
193
98
                mutter("Tag %s points at object %r that is not a commit, "
194
99
                       "ignoring", k, obj)
195
100
                continue
196
 
            yield (k, peeled, unpeeled,
197
 
                   self.branch.lookup_foreign_revision_id(peeled))
 
101
            ret[k] = self.branch.lookup_foreign_revision_id(v)
 
102
        return ret
198
103
 
199
104
    def _set_tag_dict(self, to_dict):
200
 
        extra = set(self.get_refs().keys())
 
105
        extra = set(self.repository._git.get_refs().keys())
201
106
        for k, revid in to_dict.iteritems():
202
107
            name = tag_name_to_ref(k)
203
108
            if name in extra:
204
109
                extra.remove(name)
205
110
            self.set_tag(k, revid)
206
111
        for name in extra:
207
 
            if is_tag(name):
 
112
            if name.startswith("refs/tags/"):
208
113
                del self.repository._git[name]
209
114
 
210
115
    def set_tag(self, name, revid):
211
 
        self.refs[tag_name_to_ref(name)], _ = \
 
116
        self.repository._git.refs[tag_name_to_ref(name)], _ = \
212
117
            self.branch.lookup_bzr_revision_id(revid)
213
118
 
214
119
 
215
 
class DictTagDict(tag.BasicTags):
 
120
class DictTagDict(LocalGitTagDict):
216
121
 
217
122
    def __init__(self, branch, tags):
218
123
        super(DictTagDict, self).__init__(branch)
233
138
    def supports_tags(self):
234
139
        return True
235
140
 
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
 
 
244
141
    def get_foreign_tests_branch_factory(self):
245
142
        from bzrlib.plugins.git.tests.test_branch import ForeignTestsBranchFactory
246
143
        return ForeignTestsBranchFactory()
252
149
        else:
253
150
            return LocalGitTagDict(branch)
254
151
 
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
 
 
265
152
 
266
153
class GitReadLock(object):
267
154
 
272
159
class GitWriteLock(object):
273
160
 
274
161
    def __init__(self, unlock):
275
 
        self.branch_token = None
276
162
        self.unlock = unlock
277
163
 
278
164
 
279
165
class GitBranch(ForeignBranch):
280
166
    """An adapter to git repositories for bzr Branch objects."""
281
167
 
282
 
    @property
283
 
    def control_transport(self):
284
 
        return self.bzrdir.control_transport
285
 
 
286
168
    def __init__(self, bzrdir, repository, ref, lockfiles, tagsdict=None):
287
169
        self.repository = repository
288
170
        self._format = GitBranchFormat()
289
171
        self.control_files = lockfiles
290
172
        self.bzrdir = bzrdir
291
 
        self._lock_mode = None
292
 
        self._lock_count = 0
293
173
        super(GitBranch, self).__init__(repository.get_mapping())
294
174
        if tagsdict is not None:
295
175
            self.tags = DictTagDict(self, tagsdict)
328
208
            self.ref or "HEAD")
329
209
 
330
210
    def generate_revision_history(self, revid, old_revid=None):
331
 
        if revid == NULL_REVISION:
332
 
            newhead = ZERO_SHA
333
 
        else:
334
 
            # FIXME: Check that old_revid is in the ancestry of revid
335
 
            newhead, self.mapping = self.mapping.revision_id_bzr_to_foreign(revid)
336
 
            if self.mapping is None:
337
 
                raise AssertionError
 
211
        # FIXME: Check that old_revid is in the ancestry of revid
 
212
        newhead, self.mapping = self.mapping.revision_id_bzr_to_foreign(revid)
338
213
        self._set_head(newhead)
339
214
 
340
 
    def lock_write(self, token=None):
341
 
        if token is not None:
342
 
            raise errors.TokenLockingNotSupported(self)
343
 
        if self._lock_mode:
344
 
            assert self._lock_mode == 'w'
345
 
            self._lock_count += 1
346
 
        else:
347
 
            self._lock_mode = 'w'
348
 
            self._lock_count = 1
349
 
        self.repository.lock_write()
 
215
    def lock_write(self):
 
216
        self.control_files.lock_write()
350
217
        return GitWriteLock(self.unlock)
351
218
 
352
219
    def get_stacked_on_url(self):
363
230
        pass
364
231
 
365
232
    def lock_read(self):
366
 
        if self._lock_mode:
367
 
            assert self._lock_mode in ('r', 'w')
368
 
            self._lock_count += 1
369
 
        else:
370
 
            self._lock_mode = 'r'
371
 
            self._lock_count = 1
372
 
        self.repository.lock_read()
 
233
        self.control_files.lock_read()
373
234
        return GitReadLock(self.unlock)
374
235
 
375
 
    def peek_lock_mode(self):
376
 
        return self._lock_mode
377
 
 
378
236
    def is_locked(self):
379
 
        return (self._lock_mode is not None)
 
237
        return self.control_files.is_locked()
380
238
 
381
239
    def unlock(self):
382
 
        """See Branch.unlock()."""
383
 
        self._lock_count -= 1
384
 
        if self._lock_count == 0:
385
 
            self._lock_mode = None
386
 
            self._clear_cached_state()
387
 
        self.repository.unlock()
 
240
        self.control_files.unlock()
388
241
 
389
242
    def get_physical_lock_status(self):
390
243
        return False
412
265
class LocalGitBranch(GitBranch):
413
266
    """A local Git branch."""
414
267
 
415
 
    def __init__(self, bzrdir, repository, ref, lockfiles, tagsdict=None):
416
 
        super(LocalGitBranch, self).__init__(bzrdir, repository, ref,
 
268
    def __init__(self, bzrdir, repository, name, lockfiles, tagsdict=None):
 
269
        super(LocalGitBranch, self).__init__(bzrdir, repository, name,
417
270
              lockfiles, tagsdict)
418
271
        refs = repository._git.get_refs()
419
 
        if not (ref in refs.keys() or "HEAD" in refs.keys()):
 
272
        if not (name in refs.keys() or "HEAD" in refs.keys()):
420
273
            raise errors.NotBranchError(self.base)
421
274
 
422
275
    def create_checkout(self, to_location, revision_id=None, lightweight=False,
467
320
        except KeyError:
468
321
            return None
469
322
 
470
 
    def _read_last_revision_info(self):
471
 
        last_revid = self.last_revision()
472
 
        graph = self.repository.get_graph()
473
 
        revno = graph.find_distance_to_null(last_revid,
474
 
            [(revision.NULL_REVISION, 0)])
475
 
        return revno, last_revid
476
 
 
477
 
    def set_last_revision_info(self, revno, revision_id):
478
 
        self.set_last_revision(revision_id)
479
 
        self._last_revision_info_cache = revno, revision_id
 
323
    def set_last_revision_info(self, revno, revid):
 
324
        self.set_last_revision(revid)
480
325
 
481
326
    def set_last_revision(self, revid):
482
 
        if not revid or not isinstance(revid, basestring):
483
 
            raise errors.InvalidRevisionId(revision_id=revid, branch=self)
484
 
        if revid == NULL_REVISION:
485
 
            newhead = ZERO_SHA
486
 
        else:
487
 
            (newhead, self.mapping) = self.repository.lookup_bzr_revision_id(revid)
488
 
            if self.mapping is None:
489
 
                raise AssertionError
490
 
        self._set_head(newhead)
 
327
        (newhead, self.mapping) = self.repository.lookup_bzr_revision_id(revid)
 
328
        self.head = newhead
491
329
 
492
330
    def _set_head(self, value):
493
331
        self._head = value
513
351
        return True
514
352
 
515
353
 
516
 
def _quick_lookup_revno(local_branch, remote_branch, revid):
517
 
    assert isinstance(revid, str), "was %r" % revid
518
 
    # Try in source branch first, it'll be faster
519
 
    try:
520
 
        return local_branch.revision_id_to_revno(revid)
521
 
    except errors.NoSuchRevision:
522
 
        graph = local_branch.repository.get_graph()
523
 
        try:
524
 
            return graph.find_distance_to_null(revid)
525
 
        except errors.GhostRevisionsHaveNoRevno:
526
 
            # FIXME: Check using graph.find_distance_to_null() ?
527
 
            return remote_branch.revision_id_to_revno(revid)
528
 
 
529
 
 
530
354
class GitBranchPullResult(branch.PullResult):
531
355
 
532
356
    def __init__(self):
547
371
        self._show_tag_conficts(to_file)
548
372
 
549
373
    def _lookup_revno(self, revid):
550
 
        return _quick_lookup_revno(self.target_branch, self.source_branch,
551
 
                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)
552
381
 
553
382
    def _get_old_revno(self):
554
383
        if self._old_revno is not None:
574
403
class GitBranchPushResult(branch.BranchPushResult):
575
404
 
576
405
    def _lookup_revno(self, revid):
577
 
        return _quick_lookup_revno(self.source_branch, self.target_branch,
578
 
            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)
579
413
 
580
414
    @property
581
415
    def old_revno(self):
583
417
 
584
418
    @property
585
419
    def new_revno(self):
586
 
        new_original_revno = getattr(self, "new_original_revno", None)
587
 
        if new_original_revno:
588
 
            return new_original_revno
589
 
        if getattr(self, "new_original_revid", None) is not None:
590
 
            return self._lookup_revno(self.new_original_revid)
591
420
        return self._lookup_revno(self.new_revid)
592
421
 
593
422
 
596
425
 
597
426
    @staticmethod
598
427
    def _get_branch_formats_to_test():
599
 
        try:
600
 
            default_format = branch.format_registry.get_default()
601
 
        except AttributeError:
602
 
            default_format = branch.BranchFormat._default_format
603
 
        return [
604
 
            (GitBranchFormat(), GitBranchFormat()),
605
 
            (GitBranchFormat(), default_format)]
 
428
        return []
606
429
 
607
430
    @classmethod
608
431
    def _get_interrepo(self, source, target):
609
 
        return _mod_repository.InterRepository.get(source.repository, target.repository)
 
432
        return repository.InterRepository.get(source.repository,
 
433
            target.repository)
610
434
 
611
435
    @classmethod
612
436
    def is_compatible(cls, source, target):
613
 
        if not isinstance(source, GitBranch):
614
 
            return False
615
 
        if isinstance(target, GitBranch):
616
 
            # InterLocalGitRemoteGitBranch or InterToGitBranch should be used
617
 
            return False
618
 
        if getattr(cls._get_interrepo(source, target), "fetch_objects", None) is None:
619
 
            # fetch_objects is necessary for this to work
620
 
            return False
621
 
        return True
622
 
 
623
 
    def fetch(self, stop_revision=None, fetch_tags=True):
624
 
        self.fetch_objects(stop_revision, fetch_tags=fetch_tags)
625
 
 
626
 
    def fetch_objects(self, stop_revision, fetch_tags):
 
437
        return (isinstance(source, GitBranch) and
 
438
                not isinstance(target, GitBranch) and
 
439
                (getattr(cls._get_interrepo(source, target), "fetch_objects", None) is not None))
 
440
 
 
441
    def _update_revisions(self, stop_revision=None, overwrite=False,
 
442
        graph=None, limit=None):
 
443
        """Like InterBranch.update_revisions(), but with additions.
 
444
 
 
445
        Compared to the `update_revisions()` below, this function takes a
 
446
        `limit` argument that limits how many git commits will be converted
 
447
        and returns the new git head.
 
448
        """
627
449
        interrepo = self._get_interrepo(self.source, self.target)
628
450
        def determine_wants(heads):
629
451
            if self.source.ref is not None and not self.source.ref in heads:
630
452
                raise NoSuchRef(self.source.ref, heads.keys())
631
 
 
632
 
            if stop_revision is None:
 
453
            if stop_revision is not None:
 
454
                self._last_revid = stop_revision
 
455
                head, mapping = self.source.repository.lookup_bzr_revision_id(
 
456
                    stop_revision)
 
457
            else:
633
458
                if self.source.ref is not None:
634
459
                    head = heads[self.source.ref]
635
460
                else:
636
461
                    head = heads["HEAD"]
637
462
                self._last_revid = self.source.lookup_foreign_revision_id(head)
638
 
            else:
639
 
                self._last_revid = stop_revision
640
 
            real = interrepo.get_determine_wants_revids(
641
 
                [self._last_revid], include_tags=fetch_tags)
642
 
            return real(heads)
 
463
            if self.target.repository.has_revision(self._last_revid):
 
464
                return []
 
465
            return [head]
643
466
        pack_hint, head, refs = interrepo.fetch_objects(
644
 
            determine_wants, self.source.mapping)
 
467
            determine_wants, self.source.mapping, limit=limit)
645
468
        if (pack_hint is not None and
646
469
            self.target.repository._format.pack_compresses):
647
470
            self.target.repository.pack(hint=pack_hint)
648
 
        return head, refs
649
 
 
650
 
    def _update_revisions(self, stop_revision=None, overwrite=False):
651
 
        head, refs = self.fetch_objects(stop_revision, fetch_tags=True)
 
471
        if head is not None:
 
472
            self._last_revid = self.source.lookup_foreign_revision_id(head)
652
473
        if overwrite:
653
474
            prev_last_revid = None
654
475
        else:
655
476
            prev_last_revid = self.target.last_revision()
656
477
        self.target.generate_revision_history(self._last_revid,
657
 
            prev_last_revid, self.source)
658
 
        return head, refs
 
478
            prev_last_revid)
 
479
        return head
 
480
 
 
481
    def update_revisions(self, stop_revision=None, overwrite=False,
 
482
                         graph=None):
 
483
        """See InterBranch.update_revisions()."""
 
484
        self._update_revisions(stop_revision, overwrite, graph)
659
485
 
660
486
    def pull(self, overwrite=False, stop_revision=None,
661
487
             possible_transports=None, _hook_master=None, run_hooks=True,
662
 
             _override_hook_target=None, local=False):
 
488
             _override_hook_target=None, local=False, limit=None):
663
489
        """See Branch.pull.
664
490
 
665
491
        :param _hook_master: Private parameter - set the branch to
669
495
            so it should not run its hooks.
670
496
        :param _override_hook_target: Private parameter - set the branch to be
671
497
            supplied as the target_branch to pull hooks.
 
498
        :param limit: Only import this many revisons.  `None`, the default,
 
499
            means import all revisions.
672
500
        """
673
501
        # This type of branch can't be bound.
674
502
        if local:
683
511
        try:
684
512
            # We assume that during 'pull' the target repository is closer than
685
513
            # the source one.
 
514
            graph = self.target.repository.get_graph(self.source.repository)
686
515
            (result.old_revno, result.old_revid) = \
687
516
                self.target.last_revision_info()
688
 
            result.new_git_head, remote_refs = self._update_revisions(
689
 
                stop_revision, overwrite=overwrite)
 
517
            result.new_git_head = self._update_revisions(
 
518
                stop_revision, overwrite=overwrite, graph=graph, limit=limit)
690
519
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
691
520
                overwrite)
692
521
            (result.new_revno, result.new_revid) = \
708
537
        result = branch.BranchPushResult()
709
538
        result.source_branch = self.source
710
539
        result.target_branch = self.target
 
540
        graph = self.target.repository.get_graph(self.source.repository)
711
541
        result.old_revno, result.old_revid = self.target.last_revision_info()
712
 
        result.new_git_head, remote_refs = self._update_revisions(
713
 
            stop_revision, overwrite=overwrite)
 
542
        result.new_git_head = self._update_revisions(
 
543
            stop_revision, overwrite=overwrite, graph=graph)
714
544
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
715
545
            overwrite)
716
546
        result.new_revno, result.new_revid = self.target.last_revision_info()
721
551
    """InterBranch implementation that pulls between Git branches."""
722
552
 
723
553
 
724
 
class InterLocalGitRemoteGitBranch(InterGitBranch):
 
554
class InterGitLocalRemoteBranch(InterGitBranch):
725
555
    """InterBranch that copies from a local to a remote git branch."""
726
556
 
727
557
    @staticmethod
728
558
    def _get_branch_formats_to_test():
729
 
        # FIXME
730
559
        return []
731
560
 
732
561
    @classmethod
736
565
                isinstance(target, RemoteGitBranch))
737
566
 
738
567
    def _basic_push(self, overwrite=False, stop_revision=None):
 
568
        from dulwich.protocol import ZERO_SHA
739
569
        result = GitBranchPushResult()
740
570
        result.source_branch = self.source
741
571
        result.target_branch = self.target
754
584
        return result
755
585
 
756
586
 
757
 
class InterGitLocalGitBranch(InterGitBranch):
 
587
class InterGitRemoteLocalBranch(InterGitBranch):
758
588
    """InterBranch that copies from a remote to a local git branch."""
759
589
 
760
590
    @staticmethod
761
591
    def _get_branch_formats_to_test():
762
 
        # FIXME
763
592
        return []
764
593
 
765
594
    @classmethod
766
595
    def is_compatible(self, source, target):
767
 
        return (isinstance(source, GitBranch) and
 
596
        from bzrlib.plugins.git.remote import RemoteGitBranch
 
597
        return (isinstance(source, RemoteGitBranch) and
768
598
                isinstance(target, LocalGitBranch))
769
599
 
770
600
    def _basic_push(self, overwrite=False, stop_revision=None):
774
604
        result.old_revid = self.target.last_revision()
775
605
        refs, stop_revision = self.update_refs(stop_revision)
776
606
        self.target.generate_revision_history(stop_revision, result.old_revid)
777
 
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
778
 
            source_refs=refs, overwrite=overwrite)
 
607
        self.update_tags(refs)
779
608
        result.new_revid = self.target.last_revision()
780
609
        return result
781
610
 
 
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
 
782
616
    def update_refs(self, stop_revision=None):
783
 
        interrepo = _mod_repository.InterRepository.get(self.source.repository,
 
617
        interrepo = repository.InterRepository.get(self.source.repository,
784
618
            self.target.repository)
785
619
        if stop_revision is None:
786
620
            refs = interrepo.fetch(branches=["HEAD"])
800
634
        result.old_revid = self.target.last_revision()
801
635
        refs, stop_revision = self.update_refs(stop_revision)
802
636
        self.target.generate_revision_history(stop_revision, result.old_revid)
803
 
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
804
 
            overwrite=overwrite, source_refs=refs)
 
637
        self.update_tags(refs)
805
638
        result.new_revid = self.target.last_revision()
806
639
        return result
807
640
 
808
641
 
809
642
class InterToGitBranch(branch.GenericInterBranch):
810
 
    """InterBranch implementation that pulls into a Git branch."""
 
643
    """InterBranch implementation that pulls from Git into bzr."""
811
644
 
812
645
    def __init__(self, source, target):
813
646
        super(InterToGitBranch, self).__init__(source, target)
814
 
        self.interrepo = _mod_repository.InterRepository.get(source.repository,
 
647
        self.interrepo = repository.InterRepository.get(source.repository,
815
648
                                           target.repository)
816
649
 
817
650
    @staticmethod
818
651
    def _get_branch_formats_to_test():
819
 
        try:
820
 
            default_format = branch.format_registry.get_default()
821
 
        except AttributeError:
822
 
            default_format = branch.BranchFormat._default_format
823
 
        return [(default_format, GitBranchFormat())]
 
652
        return []
824
653
 
825
654
    @classmethod
826
655
    def is_compatible(self, source, target):
827
656
        return (not isinstance(source, GitBranch) and
828
657
                isinstance(target, GitBranch))
829
658
 
 
659
    def update_revisions(self, *args, **kwargs):
 
660
        raise NoPushSupport()
 
661
 
830
662
    def _get_new_refs(self, stop_revision=None):
831
663
        if stop_revision is None:
832
 
            (stop_revno, stop_revision) = self.source.last_revision_info()
833
 
        else:
834
 
            stop_revno = self.source.revision_id_to_revno(stop_revision)
 
664
            stop_revision = self.source.last_revision()
835
665
        assert type(stop_revision) is str
836
666
        main_ref = self.target.ref or "refs/heads/master"
837
667
        refs = { main_ref: (None, stop_revision) }
838
668
        for name, revid in self.source.tags.get_tag_dict().iteritems():
839
669
            if self.source.repository.has_revision(revid):
840
670
                refs[tag_name_to_ref(name)] = (None, revid)
841
 
        return refs, main_ref, (stop_revno, stop_revision)
 
671
        return refs, main_ref
842
672
 
843
673
    def pull(self, overwrite=False, stop_revision=None, local=False,
844
 
             possible_transports=None, run_hooks=True):
 
674
             possible_transports=None):
 
675
        from dulwich.protocol import ZERO_SHA
845
676
        result = GitBranchPullResult()
846
677
        result.source_branch = self.source
847
678
        result.target_branch = self.target
848
 
        new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision)
 
679
        new_refs, main_ref = self._get_new_refs(stop_revision)
849
680
        def update_refs(old_refs):
850
681
            refs = dict(old_refs)
851
682
            # FIXME: Check for diverged branches
852
683
            refs.update(new_refs)
853
684
            return refs
854
 
        try:
855
 
            old_refs, new_refs = self.interrepo.fetch_refs(update_refs)
856
 
        except NoPushSupport:
857
 
            raise errors.NoRoundtrippingSupport(self.source, self.target)
858
 
        (result.old_revid, old_sha1) = old_refs.get(main_ref, (ZERO_SHA, NULL_REVISION))
859
 
        if result.old_revid is None:
860
 
            result.old_revid = self.target.lookup_foreign_revision_id(old_sha1)
861
 
        result.new_revid = new_refs[main_ref][1]
 
685
        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]
862
689
        return result
863
690
 
864
691
    def push(self, overwrite=False, stop_revision=None,
865
692
             _override_hook_source_branch=None):
 
693
        from dulwich.protocol import ZERO_SHA
866
694
        result = GitBranchPushResult()
867
695
        result.source_branch = self.source
868
696
        result.target_branch = self.target
869
 
        new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision)
 
697
        new_refs, main_ref = self._get_new_refs(stop_revision)
870
698
        def update_refs(old_refs):
871
699
            refs = dict(old_refs)
872
700
            # FIXME: Check for diverged branches
873
701
            refs.update(new_refs)
874
702
            return refs
875
 
        try:
876
 
            old_refs, new_refs = self.interrepo.fetch_refs(update_refs)
877
 
        except NoPushSupport:
878
 
            raise errors.NoRoundtrippingSupport(self.source, self.target)
 
703
        old_refs, new_refs = self.interrepo.fetch_refs(update_refs)
879
704
        (result.old_revid, old_sha1) = old_refs.get(main_ref, (ZERO_SHA, NULL_REVISION))
880
705
        if result.old_revid is None:
881
706
            result.old_revid = self.target.lookup_foreign_revision_id(old_sha1)
882
 
        result.new_revid = new_refs[main_ref][1]
 
707
        result.new_revid = new_refs[main_ref]
883
708
        return result
884
709
 
885
710
    def lossy_push(self, stop_revision=None):
886
711
        result = GitBranchPushResult()
887
712
        result.source_branch = self.source
888
713
        result.target_branch = self.target
889
 
        new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision)
 
714
        new_refs, main_ref = self._get_new_refs(stop_revision)
890
715
        def update_refs(old_refs):
891
716
            refs = dict(old_refs)
892
717
            # FIXME: Check for diverged branches
896
721
            update_refs)
897
722
        result.old_revid = old_refs.get(self.target.ref, (None, NULL_REVISION))[1]
898
723
        result.new_revid = new_refs[main_ref][1]
899
 
        (result.new_original_revno, result.new_original_revid) = stop_revinfo
900
724
        return result
901
725
 
902
726
 
903
 
branch.InterBranch.register_optimiser(InterGitLocalGitBranch)
 
727
branch.InterBranch.register_optimiser(InterGitRemoteLocalBranch)
904
728
branch.InterBranch.register_optimiser(InterFromGitBranch)
905
729
branch.InterBranch.register_optimiser(InterToGitBranch)
906
 
branch.InterBranch.register_optimiser(InterLocalGitRemoteGitBranch)
 
730
branch.InterBranch.register_optimiser(InterGitLocalRemoteBranch)