/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
57
    )
64
 
from bzrlib.plugins.git.unpeel_map import (
65
 
    UnpeelMap,
66
 
    )
67
58
 
68
59
from bzrlib.foreign import ForeignBranch
69
60
 
85
76
        return self._lookup_revno(self.new_revid)
86
77
 
87
78
 
88
 
class GitTags(tag.BasicTags):
89
 
    """Ref-based tag dictionary."""
 
79
class LocalGitTagDict(tag.BasicTags):
 
80
    """Dictionary with tags in a local repository."""
90
81
 
91
82
    def __init__(self, branch):
92
83
        self.branch = branch
93
84
        self.repository = branch.repository
94
85
 
95
 
    def get_refs(self):
96
 
        raise NotImplementedError(self.get_refs)
97
 
 
98
 
    def _iter_tag_refs(self, refs):
99
 
        raise NotImplementedError(self._iter_tag_refs)
100
 
 
101
 
    def _merge_to_git(self, to_tags, refs, overwrite=False):
102
 
        target_repo = to_tags.repository
103
 
        conflicts = []
104
 
        for k, v in refs.iteritems():
105
 
            if not is_tag(k):
106
 
                continue
107
 
            if overwrite or not k in target_repo._git.refs:
108
 
                target_repo._git.refs[k] = v
109
 
            elif target_repo._git.refs[k] == v:
110
 
                pass
111
 
            else:
112
 
                conflicts.append((ref_to_tag_name(k), v, target_repo.refs[k]))
113
 
        return conflicts
114
 
 
115
 
    def _merge_to_non_git(self, to_tags, refs, overwrite=False):
116
 
        unpeeled_map = defaultdict(set)
117
 
        conflicts = []
118
 
        result = dict(to_tags.get_tag_dict())
119
 
        for n, peeled, unpeeled, bzr_revid in self._iter_tag_refs(refs):
120
 
            if unpeeled is not None:
121
 
                unpeeled_map[peeled].add(unpeeled)
122
 
            if n not in result or overwrite:
123
 
                result[n] = bzr_revid
124
 
            elif result[n] == bzr_revid:
125
 
                pass
126
 
            else:
127
 
                conflicts.append((n, result[n], bzr_revid))
128
 
        to_tags._set_tag_dict(result)
129
 
        if len(unpeeled_map) > 0:
130
 
            map_file = UnpeelMap.from_repository(to_tags.branch.repository)
131
 
            map_file.update(unpeeled_map)
132
 
            map_file.save_in_repository(to_tags.branch.repository)
133
 
        return conflicts
134
 
 
135
 
    def merge_to(self, to_tags, overwrite=False, ignore_master=False,
136
 
                 source_refs=None):
137
 
        """See Tags.merge_to."""
138
 
        if source_refs is None:
139
 
            source_refs = self.get_refs()
140
 
        if self == to_tags:
141
 
            return
142
 
        if isinstance(to_tags, GitTags):
143
 
            return self._merge_to_git(to_tags, source_refs,
144
 
                                      overwrite=overwrite)
145
 
        else:
146
 
            if ignore_master:
147
 
                master = None
148
 
            else:
149
 
                master = to_tags.branch.get_master_branch()
150
 
            conflicts = self._merge_to_non_git(to_tags, source_refs,
151
 
                                              overwrite=overwrite)
152
 
            if master is not None:
153
 
                conflicts += self.merge_to(master.tags, overwrite=overwrite,
154
 
                                           source_refs=source_refs,
155
 
                                           ignore_master=ignore_master)
156
 
            return conflicts
157
 
 
158
86
    def get_tag_dict(self):
159
87
        ret = {}
160
 
        refs = self.get_refs()
161
 
        for (name, peeled, unpeeled, bzr_revid) in self._iter_tag_refs(refs):
162
 
            ret[name] = bzr_revid
163
 
        return ret
164
 
 
165
 
 
166
 
class LocalGitTagDict(GitTags):
167
 
    """Dictionary with tags in a local repository."""
168
 
 
169
 
    def __init__(self, branch):
170
 
        super(LocalGitTagDict, self).__init__(branch)
171
 
        self.refs = self.repository._git.refs
172
 
 
173
 
    def get_refs(self):
174
 
        return self.repository._git.get_refs()
175
 
 
176
 
    def _iter_tag_refs(self, refs):
177
 
        """Iterate over the tag refs.
178
 
 
179
 
        :param refs: Refs dictionary (name -> git sha1)
180
 
        :return: iterator over (name, peeled_sha1, unpeeled_sha1, bzr_revid)
181
 
        """
182
 
        for k, (peeled, unpeeled) in extract_tags(refs).iteritems():
 
88
        for k,v in extract_tags(self.repository._git.get_refs()).iteritems():
183
89
            try:
184
 
                obj = self.repository._git[peeled]
 
90
                obj = self.repository._git[v]
185
91
            except KeyError:
186
 
                mutter("Tag %s points at unknown object %s, ignoring", peeled,
187
 
                       obj)
 
92
                mutter("Tag %s points at unknown object %s, ignoring", v, obj)
188
93
                continue
189
 
            # FIXME: this shouldn't really be necessary, the repository
190
 
            # already should have these unpeeled.
191
94
            while isinstance(obj, Tag):
192
 
                peeled = obj.object[1]
193
 
                obj = self.repository._git[peeled]
 
95
                v = obj.object[1]
 
96
                obj = self.repository._git[v]
194
97
            if not isinstance(obj, Commit):
195
98
                mutter("Tag %s points at object %r that is not a commit, "
196
99
                       "ignoring", k, obj)
197
100
                continue
198
 
            yield (k, peeled, unpeeled,
199
 
                   self.branch.lookup_foreign_revision_id(peeled))
 
101
            ret[k] = self.branch.lookup_foreign_revision_id(v)
 
102
        return ret
200
103
 
201
104
    def _set_tag_dict(self, to_dict):
202
 
        extra = set(self.get_refs().keys())
 
105
        extra = set(self.repository._git.get_refs().keys())
203
106
        for k, revid in to_dict.iteritems():
204
107
            name = tag_name_to_ref(k)
205
108
            if name in extra:
206
109
                extra.remove(name)
207
110
            self.set_tag(k, revid)
208
111
        for name in extra:
209
 
            if is_tag(name):
 
112
            if name.startswith("refs/tags/"):
210
113
                del self.repository._git[name]
211
114
 
212
115
    def set_tag(self, name, revid):
213
 
        self.refs[tag_name_to_ref(name)], _ = \
 
116
        self.repository._git.refs[tag_name_to_ref(name)], _ = \
214
117
            self.branch.lookup_bzr_revision_id(revid)
215
118
 
216
119
 
217
 
class DictTagDict(tag.BasicTags):
 
120
class DictTagDict(LocalGitTagDict):
218
121
 
219
122
    def __init__(self, branch, tags):
220
123
        super(DictTagDict, self).__init__(branch)
235
138
    def supports_tags(self):
236
139
        return True
237
140
 
238
 
    def supports_leaving_lock(self):
239
 
        return False
240
 
 
241
 
    @property
242
 
    def _matchingbzrdir(self):
243
 
        from bzrlib.plugins.git.dir import LocalGitControlDirFormat
244
 
        return LocalGitControlDirFormat()
245
 
 
246
141
    def get_foreign_tests_branch_factory(self):
247
142
        from bzrlib.plugins.git.tests.test_branch import ForeignTestsBranchFactory
248
143
        return ForeignTestsBranchFactory()
254
149
        else:
255
150
            return LocalGitTagDict(branch)
256
151
 
257
 
    def initialize(self, a_bzrdir, name=None, repository=None):
258
 
        from bzrlib.plugins.git.dir import LocalGitDir
259
 
        if not isinstance(a_bzrdir, LocalGitDir):
260
 
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
261
 
        if repository is None:
262
 
            repository = a_bzrdir.open_repository()
263
 
        ref = branch_name_to_ref(name, "HEAD")
264
 
        repository._git[ref] = ZERO_SHA
265
 
        return LocalGitBranch(a_bzrdir, repository, ref, a_bzrdir._lockfiles)
266
 
 
267
152
 
268
153
class GitReadLock(object):
269
154
 
274
159
class GitWriteLock(object):
275
160
 
276
161
    def __init__(self, unlock):
277
 
        self.branch_token = None
278
162
        self.unlock = unlock
279
163
 
280
164
 
281
165
class GitBranch(ForeignBranch):
282
166
    """An adapter to git repositories for bzr Branch objects."""
283
167
 
284
 
    @property
285
 
    def control_transport(self):
286
 
        return self.bzrdir.control_transport
287
 
 
288
168
    def __init__(self, bzrdir, repository, ref, lockfiles, tagsdict=None):
289
 
        self.base = bzrdir.root_transport.base
290
169
        self.repository = repository
291
170
        self._format = GitBranchFormat()
292
171
        self.control_files = lockfiles
293
172
        self.bzrdir = bzrdir
294
 
        self._lock_mode = None
295
 
        self._lock_count = 0
296
173
        super(GitBranch, self).__init__(repository.get_mapping())
297
174
        if tagsdict is not None:
298
175
            self.tags = DictTagDict(self, tagsdict)
299
176
        self.ref = ref
300
177
        self.name = ref_to_branch_name(ref)
301
178
        self._head = None
 
179
        self.base = bzrdir.root_transport.base
302
180
 
303
181
    def _get_checkout_format(self):
304
182
        """Return the most suitable metadir for a checkout of this branch.
330
208
            self.ref or "HEAD")
331
209
 
332
210
    def generate_revision_history(self, revid, old_revid=None):
333
 
        if revid == NULL_REVISION:
334
 
            newhead = ZERO_SHA
335
 
        else:
336
 
            # FIXME: Check that old_revid is in the ancestry of revid
337
 
            newhead, self.mapping = self.mapping.revision_id_bzr_to_foreign(revid)
338
 
            if self.mapping is None:
339
 
                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)
340
213
        self._set_head(newhead)
341
214
 
342
 
    def lock_write(self, token=None):
343
 
        if token is not None:
344
 
            raise errors.TokenLockingNotSupported(self)
345
 
        if self._lock_mode:
346
 
            assert self._lock_mode == 'w'
347
 
            self._lock_count += 1
348
 
        else:
349
 
            self._lock_mode = 'w'
350
 
            self._lock_count = 1
351
 
        self.repository.lock_write()
 
215
    def lock_write(self):
 
216
        self.control_files.lock_write()
352
217
        return GitWriteLock(self.unlock)
353
218
 
354
219
    def get_stacked_on_url(self):
365
230
        pass
366
231
 
367
232
    def lock_read(self):
368
 
        if self._lock_mode:
369
 
            assert self._lock_mode in ('r', 'w')
370
 
            self._lock_count += 1
371
 
        else:
372
 
            self._lock_mode = 'r'
373
 
            self._lock_count = 1
374
 
        self.repository.lock_read()
 
233
        self.control_files.lock_read()
375
234
        return GitReadLock(self.unlock)
376
235
 
377
 
    def peek_lock_mode(self):
378
 
        return self._lock_mode
379
 
 
380
236
    def is_locked(self):
381
 
        return (self._lock_mode is not None)
 
237
        return self.control_files.is_locked()
382
238
 
383
239
    def unlock(self):
384
 
        """See Branch.unlock()."""
385
 
        self._lock_count -= 1
386
 
        if self._lock_count == 0:
387
 
            self._lock_mode = None
388
 
            self._clear_cached_state()
389
 
        self.repository.unlock()
 
240
        self.control_files.unlock()
390
241
 
391
242
    def get_physical_lock_status(self):
392
243
        return False
414
265
class LocalGitBranch(GitBranch):
415
266
    """A local Git branch."""
416
267
 
417
 
    def __init__(self, bzrdir, repository, ref, lockfiles, tagsdict=None):
418
 
        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,
419
270
              lockfiles, tagsdict)
420
271
        refs = repository._git.get_refs()
421
 
        if not (ref in refs.keys() or "HEAD" in refs.keys()):
 
272
        if not (name in refs.keys() or "HEAD" in refs.keys()):
422
273
            raise errors.NotBranchError(self.base)
423
274
 
424
275
    def create_checkout(self, to_location, revision_id=None, lightweight=False,
458
309
    def _gen_revision_history(self):
459
310
        if self.head is None:
460
311
            return []
461
 
        graph = self.repository.get_graph()
462
 
        ret = list(graph.iter_lefthand_ancestry(self.last_revision(),
463
 
            (revision.NULL_REVISION, )))
 
312
        ret = list(self.repository.iter_reverse_revision_history(
 
313
            self.last_revision()))
464
314
        ret.reverse()
465
315
        return ret
466
316
 
470
320
        except KeyError:
471
321
            return None
472
322
 
473
 
    def _read_last_revision_info(self):
474
 
        last_revid = self.last_revision()
475
 
        graph = self.repository.get_graph()
476
 
        revno = graph.find_distance_to_null(last_revid,
477
 
            [(revision.NULL_REVISION, 0)])
478
 
        return revno, last_revid
479
 
 
480
 
    def set_last_revision_info(self, revno, revision_id):
481
 
        self.set_last_revision(revision_id)
482
 
        self._last_revision_info_cache = revno, revision_id
 
323
    def set_last_revision_info(self, revno, revid):
 
324
        self.set_last_revision(revid)
483
325
 
484
326
    def set_last_revision(self, revid):
485
 
        if not revid or not isinstance(revid, basestring):
486
 
            raise errors.InvalidRevisionId(revision_id=revid, branch=self)
487
 
        if revid == NULL_REVISION:
488
 
            newhead = ZERO_SHA
489
 
        else:
490
 
            (newhead, self.mapping) = self.repository.lookup_bzr_revision_id(revid)
491
 
            if self.mapping is None:
492
 
                raise AssertionError
493
 
        self._set_head(newhead)
 
327
        (newhead, self.mapping) = self.repository.lookup_bzr_revision_id(revid)
 
328
        self.head = newhead
494
329
 
495
330
    def _set_head(self, value):
496
331
        self._head = value
516
351
        return True
517
352
 
518
353
 
519
 
def _quick_lookup_revno(local_branch, remote_branch, revid):
520
 
    assert isinstance(revid, str), "was %r" % revid
521
 
    # Try in source branch first, it'll be faster
522
 
    try:
523
 
        return local_branch.revision_id_to_revno(revid)
524
 
    except errors.NoSuchRevision:
525
 
        graph = local_branch.repository.get_graph()
526
 
        try:
527
 
            return graph.find_distance_to_null(revid)
528
 
        except errors.GhostRevisionsHaveNoRevno:
529
 
            # FIXME: Check using graph.find_distance_to_null() ?
530
 
            return remote_branch.revision_id_to_revno(revid)
531
 
 
532
 
 
533
354
class GitBranchPullResult(branch.PullResult):
534
355
 
535
356
    def __init__(self):
550
371
        self._show_tag_conficts(to_file)
551
372
 
552
373
    def _lookup_revno(self, revid):
553
 
        return _quick_lookup_revno(self.target_branch, self.source_branch,
554
 
                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)
555
381
 
556
382
    def _get_old_revno(self):
557
383
        if self._old_revno is not None:
577
403
class GitBranchPushResult(branch.BranchPushResult):
578
404
 
579
405
    def _lookup_revno(self, revid):
580
 
        return _quick_lookup_revno(self.source_branch, self.target_branch,
581
 
            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)
582
413
 
583
414
    @property
584
415
    def old_revno(self):
586
417
 
587
418
    @property
588
419
    def new_revno(self):
589
 
        new_original_revno = getattr(self, "new_original_revno", None)
590
 
        if new_original_revno:
591
 
            return new_original_revno
592
 
        if getattr(self, "new_original_revid", None) is not None:
593
 
            return self._lookup_revno(self.new_original_revid)
594
420
        return self._lookup_revno(self.new_revid)
595
421
 
596
422
 
599
425
 
600
426
    @staticmethod
601
427
    def _get_branch_formats_to_test():
602
 
        try:
603
 
            default_format = branch.format_registry.get_default()
604
 
        except AttributeError:
605
 
            default_format = branch.BranchFormat._default_format
606
 
        return [
607
 
            (GitBranchFormat(), GitBranchFormat()),
608
 
            (GitBranchFormat(), default_format)]
 
428
        return []
609
429
 
610
430
    @classmethod
611
431
    def _get_interrepo(self, source, target):
612
 
        return _mod_repository.InterRepository.get(source.repository, target.repository)
 
432
        return repository.InterRepository.get(source.repository,
 
433
            target.repository)
613
434
 
614
435
    @classmethod
615
436
    def is_compatible(cls, source, target):
616
 
        if not isinstance(source, GitBranch):
617
 
            return False
618
 
        if isinstance(target, GitBranch):
619
 
            # InterLocalGitRemoteGitBranch or InterToGitBranch should be used
620
 
            return False
621
 
        if getattr(cls._get_interrepo(source, target), "fetch_objects", None) is None:
622
 
            # fetch_objects is necessary for this to work
623
 
            return False
624
 
        return True
625
 
 
626
 
    def fetch(self, stop_revision=None, fetch_tags=True, limit=None):
627
 
        self.fetch_objects(stop_revision, fetch_tags=fetch_tags, limit=limit)
628
 
 
629
 
    def fetch_objects(self, stop_revision, fetch_tags, limit=None):
 
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
        """
630
449
        interrepo = self._get_interrepo(self.source, self.target)
631
450
        def determine_wants(heads):
632
451
            if self.source.ref is not None and not self.source.ref in heads:
633
452
                raise NoSuchRef(self.source.ref, heads.keys())
634
 
 
635
 
            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:
636
458
                if self.source.ref is not None:
637
459
                    head = heads[self.source.ref]
638
460
                else:
639
461
                    head = heads["HEAD"]
640
462
                self._last_revid = self.source.lookup_foreign_revision_id(head)
641
 
            else:
642
 
                self._last_revid = stop_revision
643
 
            real = interrepo.get_determine_wants_revids(
644
 
                [self._last_revid], include_tags=fetch_tags)
645
 
            return real(heads)
 
463
            if self.target.repository.has_revision(self._last_revid):
 
464
                return []
 
465
            return [head]
646
466
        pack_hint, head, refs = interrepo.fetch_objects(
647
467
            determine_wants, self.source.mapping, limit=limit)
648
468
        if (pack_hint is not None and
649
469
            self.target.repository._format.pack_compresses):
650
470
            self.target.repository.pack(hint=pack_hint)
651
 
        return head, refs
652
 
 
653
 
    def _update_revisions(self, stop_revision=None, overwrite=False):
654
 
        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)
655
473
        if overwrite:
656
474
            prev_last_revid = None
657
475
        else:
658
476
            prev_last_revid = self.target.last_revision()
659
477
        self.target.generate_revision_history(self._last_revid,
660
 
            prev_last_revid, self.source)
661
 
        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)
662
485
 
663
486
    def pull(self, overwrite=False, stop_revision=None,
664
487
             possible_transports=None, _hook_master=None, run_hooks=True,
665
 
             _override_hook_target=None, local=False):
 
488
             _override_hook_target=None, local=False, limit=None):
666
489
        """See Branch.pull.
667
490
 
668
491
        :param _hook_master: Private parameter - set the branch to
672
495
            so it should not run its hooks.
673
496
        :param _override_hook_target: Private parameter - set the branch to be
674
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.
675
500
        """
676
501
        # This type of branch can't be bound.
677
502
        if local:
686
511
        try:
687
512
            # We assume that during 'pull' the target repository is closer than
688
513
            # the source one.
 
514
            graph = self.target.repository.get_graph(self.source.repository)
689
515
            (result.old_revno, result.old_revid) = \
690
516
                self.target.last_revision_info()
691
 
            result.new_git_head, remote_refs = self._update_revisions(
692
 
                stop_revision, overwrite=overwrite)
 
517
            result.new_git_head = self._update_revisions(
 
518
                stop_revision, overwrite=overwrite, graph=graph, limit=limit)
693
519
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
694
520
                overwrite)
695
521
            (result.new_revno, result.new_revid) = \
711
537
        result = branch.BranchPushResult()
712
538
        result.source_branch = self.source
713
539
        result.target_branch = self.target
 
540
        graph = self.target.repository.get_graph(self.source.repository)
714
541
        result.old_revno, result.old_revid = self.target.last_revision_info()
715
 
        result.new_git_head, remote_refs = self._update_revisions(
716
 
            stop_revision, overwrite=overwrite)
 
542
        result.new_git_head = self._update_revisions(
 
543
            stop_revision, overwrite=overwrite, graph=graph)
717
544
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
718
545
            overwrite)
719
546
        result.new_revno, result.new_revid = self.target.last_revision_info()
724
551
    """InterBranch implementation that pulls between Git branches."""
725
552
 
726
553
 
727
 
class InterLocalGitRemoteGitBranch(InterGitBranch):
 
554
class InterGitLocalRemoteBranch(InterGitBranch):
728
555
    """InterBranch that copies from a local to a remote git branch."""
729
556
 
730
557
    @staticmethod
731
558
    def _get_branch_formats_to_test():
732
 
        # FIXME
733
559
        return []
734
560
 
735
561
    @classmethod
739
565
                isinstance(target, RemoteGitBranch))
740
566
 
741
567
    def _basic_push(self, overwrite=False, stop_revision=None):
 
568
        from dulwich.protocol import ZERO_SHA
742
569
        result = GitBranchPushResult()
743
570
        result.source_branch = self.source
744
571
        result.target_branch = self.target
746
573
            stop_revision = self.source.last_revision()
747
574
        # FIXME: Check for diverged branches
748
575
        def get_changed_refs(old_refs):
749
 
            old_ref = old_refs.get(self.target.ref, ZERO_SHA)
750
 
            result.old_revid = self.target.lookup_foreign_revision_id(old_ref)
 
576
            result.old_revid = self.target.lookup_foreign_revision_id(old_refs.get(self.target.ref, ZERO_SHA))
751
577
            refs = { self.target.ref: self.source.repository.lookup_bzr_revision_id(stop_revision)[0] }
752
578
            result.new_revid = stop_revision
753
579
            for name, sha in self.source.repository._git.refs.as_dict("refs/tags").iteritems():
758
584
        return result
759
585
 
760
586
 
761
 
class InterGitLocalGitBranch(InterGitBranch):
 
587
class InterGitRemoteLocalBranch(InterGitBranch):
762
588
    """InterBranch that copies from a remote to a local git branch."""
763
589
 
764
590
    @staticmethod
765
591
    def _get_branch_formats_to_test():
766
 
        # FIXME
767
592
        return []
768
593
 
769
594
    @classmethod
770
595
    def is_compatible(self, source, target):
771
 
        return (isinstance(source, GitBranch) and
 
596
        from bzrlib.plugins.git.remote import RemoteGitBranch
 
597
        return (isinstance(source, RemoteGitBranch) and
772
598
                isinstance(target, LocalGitBranch))
773
599
 
774
600
    def _basic_push(self, overwrite=False, stop_revision=None):
778
604
        result.old_revid = self.target.last_revision()
779
605
        refs, stop_revision = self.update_refs(stop_revision)
780
606
        self.target.generate_revision_history(stop_revision, result.old_revid)
781
 
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
782
 
            source_refs=refs, overwrite=overwrite)
 
607
        self.update_tags(refs)
783
608
        result.new_revid = self.target.last_revision()
784
609
        return result
785
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
 
786
616
    def update_refs(self, stop_revision=None):
787
 
        interrepo = _mod_repository.InterRepository.get(self.source.repository,
 
617
        interrepo = repository.InterRepository.get(self.source.repository,
788
618
            self.target.repository)
789
619
        if stop_revision is None:
790
620
            refs = interrepo.fetch(branches=["HEAD"])
804
634
        result.old_revid = self.target.last_revision()
805
635
        refs, stop_revision = self.update_refs(stop_revision)
806
636
        self.target.generate_revision_history(stop_revision, result.old_revid)
807
 
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
808
 
            overwrite=overwrite, source_refs=refs)
 
637
        self.update_tags(refs)
809
638
        result.new_revid = self.target.last_revision()
810
639
        return result
811
640
 
812
641
 
813
642
class InterToGitBranch(branch.GenericInterBranch):
814
 
    """InterBranch implementation that pulls into a Git branch."""
 
643
    """InterBranch implementation that pulls from Git into bzr."""
815
644
 
816
645
    def __init__(self, source, target):
817
646
        super(InterToGitBranch, self).__init__(source, target)
818
 
        self.interrepo = _mod_repository.InterRepository.get(source.repository,
 
647
        self.interrepo = repository.InterRepository.get(source.repository,
819
648
                                           target.repository)
820
649
 
821
650
    @staticmethod
822
651
    def _get_branch_formats_to_test():
823
 
        try:
824
 
            default_format = branch.format_registry.get_default()
825
 
        except AttributeError:
826
 
            default_format = branch.BranchFormat._default_format
827
 
        return [(default_format, GitBranchFormat())]
 
652
        return []
828
653
 
829
654
    @classmethod
830
655
    def is_compatible(self, source, target):
831
656
        return (not isinstance(source, GitBranch) and
832
657
                isinstance(target, GitBranch))
833
658
 
 
659
    def update_revisions(self, *args, **kwargs):
 
660
        raise NoPushSupport()
 
661
 
834
662
    def _get_new_refs(self, stop_revision=None):
835
663
        if stop_revision is None:
836
 
            (stop_revno, stop_revision) = self.source.last_revision_info()
837
 
        else:
838
 
            stop_revno = self.source.revision_id_to_revno(stop_revision)
 
664
            stop_revision = self.source.last_revision()
839
665
        assert type(stop_revision) is str
840
666
        main_ref = self.target.ref or "refs/heads/master"
841
667
        refs = { main_ref: (None, stop_revision) }
842
668
        for name, revid in self.source.tags.get_tag_dict().iteritems():
843
669
            if self.source.repository.has_revision(revid):
844
670
                refs[tag_name_to_ref(name)] = (None, revid)
845
 
        return refs, main_ref, (stop_revno, stop_revision)
 
671
        return refs, main_ref
846
672
 
847
673
    def pull(self, overwrite=False, stop_revision=None, local=False,
848
 
             possible_transports=None, run_hooks=True):
 
674
             possible_transports=None):
 
675
        from dulwich.protocol import ZERO_SHA
849
676
        result = GitBranchPullResult()
850
677
        result.source_branch = self.source
851
678
        result.target_branch = self.target
852
 
        new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision)
 
679
        new_refs, main_ref = self._get_new_refs(stop_revision)
853
680
        def update_refs(old_refs):
854
681
            refs = dict(old_refs)
855
682
            # FIXME: Check for diverged branches
856
683
            refs.update(new_refs)
857
684
            return refs
858
 
        try:
859
 
            old_refs, new_refs = self.interrepo.fetch_refs(update_refs)
860
 
        except NoPushSupport:
861
 
            raise errors.NoRoundtrippingSupport(self.source, self.target)
862
 
        (result.old_revid, old_sha1) = old_refs.get(main_ref, (ZERO_SHA, NULL_REVISION))
863
 
        if result.old_revid is None:
864
 
            result.old_revid = self.target.lookup_foreign_revision_id(old_sha1)
865
 
        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]
866
689
        return result
867
690
 
868
 
    def push(self, overwrite=False, stop_revision=None, lossy=False,
 
691
    def push(self, overwrite=False, stop_revision=None,
869
692
             _override_hook_source_branch=None):
 
693
        from dulwich.protocol import ZERO_SHA
870
694
        result = GitBranchPushResult()
871
695
        result.source_branch = self.source
872
696
        result.target_branch = self.target
873
 
        new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision)
 
697
        new_refs, main_ref = self._get_new_refs(stop_revision)
874
698
        def update_refs(old_refs):
875
699
            refs = dict(old_refs)
876
700
            # FIXME: Check for diverged branches
877
701
            refs.update(new_refs)
878
702
            return refs
879
 
        if lossy:
880
 
            result.revidmap, old_refs, new_refs = self.interrepo.dfetch_refs(
881
 
                update_refs)
882
 
        else:
883
 
            try:
884
 
                old_refs, new_refs = self.interrepo.fetch_refs(update_refs)
885
 
            except NoPushSupport:
886
 
                raise errors.NoRoundtrippingSupport(self.source, self.target)
 
703
        old_refs, new_refs = self.interrepo.fetch_refs(update_refs)
887
704
        (result.old_revid, old_sha1) = old_refs.get(main_ref, (ZERO_SHA, NULL_REVISION))
888
705
        if result.old_revid is None:
889
706
            result.old_revid = self.target.lookup_foreign_revision_id(old_sha1)
890
 
        result.new_revid = new_refs[main_ref][1]
891
 
        (result.new_original_revno, result.new_original_revid) = stop_revinfo
 
707
        result.new_revid = new_refs[main_ref]
892
708
        return result
893
709
 
894
710
    def lossy_push(self, stop_revision=None):
895
 
        # For compatibility with bzr < 2.4
896
 
        return self.push(lossy=True, stop_revision=stop_revision)
897
 
 
898
 
 
899
 
branch.InterBranch.register_optimiser(InterGitLocalGitBranch)
 
711
        result = GitBranchPushResult()
 
712
        result.source_branch = self.source
 
713
        result.target_branch = self.target
 
714
        new_refs, main_ref = self._get_new_refs(stop_revision)
 
715
        def update_refs(old_refs):
 
716
            refs = dict(old_refs)
 
717
            # FIXME: Check for diverged branches
 
718
            refs.update(new_refs)
 
719
            return refs
 
720
        result.revidmap, old_refs, new_refs = self.interrepo.dfetch_refs(
 
721
            update_refs)
 
722
        result.old_revid = old_refs.get(self.target.ref, (None, NULL_REVISION))[1]
 
723
        result.new_revid = new_refs[main_ref][1]
 
724
        return result
 
725
 
 
726
 
 
727
branch.InterBranch.register_optimiser(InterGitRemoteLocalBranch)
900
728
branch.InterBranch.register_optimiser(InterFromGitBranch)
901
729
branch.InterBranch.register_optimiser(InterToGitBranch)
902
 
branch.InterBranch.register_optimiser(InterLocalGitRemoteGitBranch)
 
730
branch.InterBranch.register_optimiser(InterGitLocalRemoteBranch)