/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

Cope with tuples in refs dictionary.

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
 
                       peeled)
 
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)
224
127
        return self._tags
225
128
 
226
129
 
227
 
class GitSymrefBranchFormat(branch.BranchFormat):
228
 
 
229
 
    def get_format_description(self):
230
 
        return 'Git Symbolic Reference Branch'
231
 
 
232
 
    def network_name(self):
233
 
        return "git"
234
 
 
235
 
    def get_reference(self, controldir, name=None):
236
 
        return controldir.get_branch_reference(name)
237
 
 
238
 
    def set_reference(self, controldir, name, target):
239
 
        return controldir.set_branch_reference(name, target)
240
 
 
241
 
 
242
130
class GitBranchFormat(branch.BranchFormat):
243
131
 
244
132
    def get_format_description(self):
250
138
    def supports_tags(self):
251
139
        return True
252
140
 
253
 
    def supports_leaving_lock(self):
254
 
        return False
255
 
 
256
 
    @property
257
 
    def _matchingbzrdir(self):
258
 
        from bzrlib.plugins.git.dir import LocalGitControlDirFormat
259
 
        return LocalGitControlDirFormat()
260
 
 
261
141
    def get_foreign_tests_branch_factory(self):
262
142
        from bzrlib.plugins.git.tests.test_branch import ForeignTestsBranchFactory
263
143
        return ForeignTestsBranchFactory()
269
149
        else:
270
150
            return LocalGitTagDict(branch)
271
151
 
272
 
    def initialize(self, a_bzrdir, name=None, repository=None):
273
 
        from bzrlib.plugins.git.dir import LocalGitDir
274
 
        if not isinstance(a_bzrdir, LocalGitDir):
275
 
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
276
 
        if repository is None:
277
 
            repository = a_bzrdir.open_repository()
278
 
        ref = branch_name_to_ref(name, "HEAD")
279
 
        repository._git[ref] = ZERO_SHA
280
 
        return LocalGitBranch(a_bzrdir, repository, ref, a_bzrdir._lockfiles)
281
 
 
282
152
 
283
153
class GitReadLock(object):
284
154
 
289
159
class GitWriteLock(object):
290
160
 
291
161
    def __init__(self, unlock):
292
 
        self.branch_token = None
293
162
        self.unlock = unlock
294
163
 
295
164
 
296
165
class GitBranch(ForeignBranch):
297
166
    """An adapter to git repositories for bzr Branch objects."""
298
167
 
299
 
    @property
300
 
    def control_transport(self):
301
 
        return self.bzrdir.control_transport
302
 
 
303
168
    def __init__(self, bzrdir, repository, ref, lockfiles, tagsdict=None):
304
 
        self.base = bzrdir.root_transport.base
305
169
        self.repository = repository
306
170
        self._format = GitBranchFormat()
307
171
        self.control_files = lockfiles
308
172
        self.bzrdir = bzrdir
309
 
        self._lock_mode = None
310
 
        self._lock_count = 0
311
173
        super(GitBranch, self).__init__(repository.get_mapping())
312
174
        if tagsdict is not None:
313
175
            self.tags = DictTagDict(self, tagsdict)
314
176
        self.ref = ref
315
177
        self.name = ref_to_branch_name(ref)
316
178
        self._head = None
 
179
        self.base = bzrdir.root_transport.base
317
180
 
318
181
    def _get_checkout_format(self):
319
182
        """Return the most suitable metadir for a checkout of this branch.
342
205
 
343
206
    def __repr__(self):
344
207
        return "<%s(%r, %r)>" % (self.__class__.__name__, self.repository.base,
345
 
            self.name)
 
208
            self.ref or "HEAD")
346
209
 
347
210
    def generate_revision_history(self, revid, old_revid=None):
348
 
        if revid == NULL_REVISION:
349
 
            newhead = ZERO_SHA
350
 
        else:
351
 
            # FIXME: Check that old_revid is in the ancestry of revid
352
 
            newhead, self.mapping = self.repository.lookup_bzr_revision_id(revid)
353
 
            if self.mapping is None:
354
 
                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)
355
213
        self._set_head(newhead)
356
214
 
357
 
    def lock_write(self, token=None):
358
 
        if token is not None:
359
 
            raise errors.TokenLockingNotSupported(self)
360
 
        if self._lock_mode:
361
 
            assert self._lock_mode == 'w'
362
 
            self._lock_count += 1
363
 
        else:
364
 
            self._lock_mode = 'w'
365
 
            self._lock_count = 1
366
 
        self.repository.lock_write()
 
215
    def lock_write(self):
 
216
        self.control_files.lock_write()
367
217
        return GitWriteLock(self.unlock)
368
218
 
369
219
    def get_stacked_on_url(self):
380
230
        pass
381
231
 
382
232
    def lock_read(self):
383
 
        if self._lock_mode:
384
 
            assert self._lock_mode in ('r', 'w')
385
 
            self._lock_count += 1
386
 
        else:
387
 
            self._lock_mode = 'r'
388
 
            self._lock_count = 1
389
 
        self.repository.lock_read()
 
233
        self.control_files.lock_read()
390
234
        return GitReadLock(self.unlock)
391
235
 
392
 
    def peek_lock_mode(self):
393
 
        return self._lock_mode
394
 
 
395
236
    def is_locked(self):
396
 
        return (self._lock_mode is not None)
 
237
        return self.control_files.is_locked()
397
238
 
398
239
    def unlock(self):
399
 
        """See Branch.unlock()."""
400
 
        self._lock_count -= 1
401
 
        if self._lock_count == 0:
402
 
            self._lock_mode = None
403
 
            self._clear_cached_state()
404
 
        self.repository.unlock()
 
240
        self.control_files.unlock()
405
241
 
406
242
    def get_physical_lock_status(self):
407
243
        return False
429
265
class LocalGitBranch(GitBranch):
430
266
    """A local Git branch."""
431
267
 
432
 
    def __init__(self, bzrdir, repository, ref, lockfiles, tagsdict=None):
433
 
        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,
434
270
              lockfiles, tagsdict)
435
271
        refs = repository._git.get_refs()
436
 
        if not (ref in refs.keys() or "HEAD" in refs.keys()):
 
272
        if not (name in refs.keys() or "HEAD" in refs.keys()):
437
273
            raise errors.NotBranchError(self.base)
438
274
 
439
275
    def create_checkout(self, to_location, revision_id=None, lightweight=False,
473
309
    def _gen_revision_history(self):
474
310
        if self.head is None:
475
311
            return []
476
 
        graph = self.repository.get_graph()
477
 
        ret = list(graph.iter_lefthand_ancestry(self.last_revision(),
478
 
            (revision.NULL_REVISION, )))
 
312
        ret = list(self.repository.iter_reverse_revision_history(
 
313
            self.last_revision()))
479
314
        ret.reverse()
480
315
        return ret
481
316
 
485
320
        except KeyError:
486
321
            return None
487
322
 
488
 
    def _read_last_revision_info(self):
489
 
        last_revid = self.last_revision()
490
 
        graph = self.repository.get_graph()
491
 
        revno = graph.find_distance_to_null(last_revid,
492
 
            [(revision.NULL_REVISION, 0)])
493
 
        return revno, last_revid
494
 
 
495
 
    def set_last_revision_info(self, revno, revision_id):
496
 
        self.set_last_revision(revision_id)
497
 
        self._last_revision_info_cache = revno, revision_id
 
323
    def set_last_revision_info(self, revno, revid):
 
324
        self.set_last_revision(revid)
498
325
 
499
326
    def set_last_revision(self, revid):
500
 
        if not revid or not isinstance(revid, basestring):
501
 
            raise errors.InvalidRevisionId(revision_id=revid, branch=self)
502
 
        if revid == NULL_REVISION:
503
 
            newhead = ZERO_SHA
504
 
        else:
505
 
            (newhead, self.mapping) = self.repository.lookup_bzr_revision_id(revid)
506
 
            if self.mapping is None:
507
 
                raise AssertionError
508
 
        self._set_head(newhead)
 
327
        (newhead, self.mapping) = self.repository.lookup_bzr_revision_id(revid)
 
328
        self.head = newhead
509
329
 
510
330
    def _set_head(self, value):
511
331
        self._head = value
531
351
        return True
532
352
 
533
353
 
534
 
def _quick_lookup_revno(local_branch, remote_branch, revid):
535
 
    assert isinstance(revid, str), "was %r" % revid
536
 
    # Try in source branch first, it'll be faster
537
 
    try:
538
 
        return local_branch.revision_id_to_revno(revid)
539
 
    except errors.NoSuchRevision:
540
 
        graph = local_branch.repository.get_graph()
541
 
        try:
542
 
            return graph.find_distance_to_null(revid, [])
543
 
        except errors.GhostRevisionsHaveNoRevno:
544
 
            # FIXME: Check using graph.find_distance_to_null() ?
545
 
            return remote_branch.revision_id_to_revno(revid)
546
 
 
547
 
 
548
354
class GitBranchPullResult(branch.PullResult):
549
355
 
550
356
    def __init__(self):
565
371
        self._show_tag_conficts(to_file)
566
372
 
567
373
    def _lookup_revno(self, revid):
568
 
        return _quick_lookup_revno(self.target_branch, self.source_branch,
569
 
                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)
570
381
 
571
382
    def _get_old_revno(self):
572
383
        if self._old_revno is not None:
592
403
class GitBranchPushResult(branch.BranchPushResult):
593
404
 
594
405
    def _lookup_revno(self, revid):
595
 
        return _quick_lookup_revno(self.source_branch, self.target_branch,
596
 
            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)
597
413
 
598
414
    @property
599
415
    def old_revno(self):
601
417
 
602
418
    @property
603
419
    def new_revno(self):
604
 
        new_original_revno = getattr(self, "new_original_revno", None)
605
 
        if new_original_revno:
606
 
            return new_original_revno
607
 
        if getattr(self, "new_original_revid", None) is not None:
608
 
            return self._lookup_revno(self.new_original_revid)
609
420
        return self._lookup_revno(self.new_revid)
610
421
 
611
422
 
614
425
 
615
426
    @staticmethod
616
427
    def _get_branch_formats_to_test():
617
 
        try:
618
 
            default_format = branch.format_registry.get_default()
619
 
        except AttributeError:
620
 
            default_format = branch.BranchFormat._default_format
621
 
        return [
622
 
            (GitBranchFormat(), GitBranchFormat()),
623
 
            (GitBranchFormat(), default_format)]
 
428
        return []
624
429
 
625
430
    @classmethod
626
431
    def _get_interrepo(self, source, target):
627
 
        return _mod_repository.InterRepository.get(source.repository, target.repository)
 
432
        return repository.InterRepository.get(source.repository,
 
433
            target.repository)
628
434
 
629
435
    @classmethod
630
436
    def is_compatible(cls, source, target):
631
 
        if not isinstance(source, GitBranch):
632
 
            return False
633
 
        if isinstance(target, GitBranch):
634
 
            # InterLocalGitRemoteGitBranch or InterToGitBranch should be used
635
 
            return False
636
 
        if getattr(cls._get_interrepo(source, target), "fetch_objects", None) is None:
637
 
            # fetch_objects is necessary for this to work
638
 
            return False
639
 
        return True
640
 
 
641
 
    def fetch(self, stop_revision=None, fetch_tags=None, limit=None):
642
 
        self.fetch_objects(stop_revision, fetch_tags=fetch_tags, limit=limit)
643
 
 
644
 
    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
        """
645
449
        interrepo = self._get_interrepo(self.source, self.target)
646
 
        if fetch_tags is None:
647
 
            c = self.source.get_config()
648
 
            fetch_tags = c.get_user_option_as_bool('branch.fetch_tags')
649
450
        def determine_wants(heads):
650
451
            if self.source.ref is not None and not self.source.ref in heads:
651
452
                raise NoSuchRef(self.source.ref, heads.keys())
652
 
 
653
 
            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:
654
458
                if self.source.ref is not None:
655
459
                    head = heads[self.source.ref]
656
460
                else:
657
461
                    head = heads["HEAD"]
658
462
                self._last_revid = self.source.lookup_foreign_revision_id(head)
659
 
            else:
660
 
                self._last_revid = stop_revision
661
 
            real = interrepo.get_determine_wants_revids(
662
 
                [self._last_revid], include_tags=fetch_tags)
663
 
            return real(heads)
 
463
            if self.target.repository.has_revision(self._last_revid):
 
464
                return []
 
465
            return [head]
664
466
        pack_hint, head, refs = interrepo.fetch_objects(
665
467
            determine_wants, self.source.mapping, limit=limit)
666
468
        if (pack_hint is not None and
667
469
            self.target.repository._format.pack_compresses):
668
470
            self.target.repository.pack(hint=pack_hint)
669
 
        return head, refs
670
 
 
671
 
    def _update_revisions(self, stop_revision=None, overwrite=False):
672
 
        head, refs = self.fetch_objects(stop_revision, fetch_tags=None)
 
471
        if head is not None:
 
472
            self._last_revid = self.source.lookup_foreign_revision_id(head)
673
473
        if overwrite:
674
474
            prev_last_revid = None
675
475
        else:
676
476
            prev_last_revid = self.target.last_revision()
677
477
        self.target.generate_revision_history(self._last_revid,
678
 
            prev_last_revid, self.source)
679
 
        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)
680
485
 
681
486
    def pull(self, overwrite=False, stop_revision=None,
682
487
             possible_transports=None, _hook_master=None, run_hooks=True,
683
 
             _override_hook_target=None, local=False):
 
488
             _override_hook_target=None, local=False, limit=None):
684
489
        """See Branch.pull.
685
490
 
686
491
        :param _hook_master: Private parameter - set the branch to
690
495
            so it should not run its hooks.
691
496
        :param _override_hook_target: Private parameter - set the branch to be
692
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.
693
500
        """
694
501
        # This type of branch can't be bound.
695
502
        if local:
704
511
        try:
705
512
            # We assume that during 'pull' the target repository is closer than
706
513
            # the source one.
 
514
            graph = self.target.repository.get_graph(self.source.repository)
707
515
            (result.old_revno, result.old_revid) = \
708
516
                self.target.last_revision_info()
709
 
            result.new_git_head, remote_refs = self._update_revisions(
710
 
                stop_revision, overwrite=overwrite)
 
517
            result.new_git_head = self._update_revisions(
 
518
                stop_revision, overwrite=overwrite, graph=graph, limit=limit)
711
519
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
712
520
                overwrite)
713
521
            (result.new_revno, result.new_revid) = \
729
537
        result = branch.BranchPushResult()
730
538
        result.source_branch = self.source
731
539
        result.target_branch = self.target
 
540
        graph = self.target.repository.get_graph(self.source.repository)
732
541
        result.old_revno, result.old_revid = self.target.last_revision_info()
733
 
        result.new_git_head, remote_refs = self._update_revisions(
734
 
            stop_revision, overwrite=overwrite)
 
542
        result.new_git_head = self._update_revisions(
 
543
            stop_revision, overwrite=overwrite, graph=graph)
735
544
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
736
545
            overwrite)
737
546
        result.new_revno, result.new_revid = self.target.last_revision_info()
742
551
    """InterBranch implementation that pulls between Git branches."""
743
552
 
744
553
 
745
 
class InterLocalGitRemoteGitBranch(InterGitBranch):
 
554
class InterGitLocalRemoteBranch(InterGitBranch):
746
555
    """InterBranch that copies from a local to a remote git branch."""
747
556
 
748
557
    @staticmethod
749
558
    def _get_branch_formats_to_test():
750
 
        # FIXME
751
559
        return []
752
560
 
753
561
    @classmethod
757
565
                isinstance(target, RemoteGitBranch))
758
566
 
759
567
    def _basic_push(self, overwrite=False, stop_revision=None):
 
568
        from dulwich.protocol import ZERO_SHA
760
569
        result = GitBranchPushResult()
761
570
        result.source_branch = self.source
762
571
        result.target_branch = self.target
764
573
            stop_revision = self.source.last_revision()
765
574
        # FIXME: Check for diverged branches
766
575
        def get_changed_refs(old_refs):
767
 
            old_ref = old_refs.get(self.target.ref, ZERO_SHA)
768
 
            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))
769
577
            refs = { self.target.ref: self.source.repository.lookup_bzr_revision_id(stop_revision)[0] }
770
578
            result.new_revid = stop_revision
771
579
            for name, sha in self.source.repository._git.refs.as_dict("refs/tags").iteritems():
776
584
        return result
777
585
 
778
586
 
779
 
class InterGitLocalGitBranch(InterGitBranch):
 
587
class InterGitRemoteLocalBranch(InterGitBranch):
780
588
    """InterBranch that copies from a remote to a local git branch."""
781
589
 
782
590
    @staticmethod
783
591
    def _get_branch_formats_to_test():
784
 
        # FIXME
785
592
        return []
786
593
 
787
594
    @classmethod
788
595
    def is_compatible(self, source, target):
789
 
        return (isinstance(source, GitBranch) and
 
596
        from bzrlib.plugins.git.remote import RemoteGitBranch
 
597
        return (isinstance(source, RemoteGitBranch) and
790
598
                isinstance(target, LocalGitBranch))
791
599
 
792
600
    def _basic_push(self, overwrite=False, stop_revision=None):
793
 
        result = GitBranchPushResult()
 
601
        result = branch.BranchPushResult()
794
602
        result.source_branch = self.source
795
603
        result.target_branch = self.target
796
604
        result.old_revid = self.target.last_revision()
797
605
        refs, stop_revision = self.update_refs(stop_revision)
798
606
        self.target.generate_revision_history(stop_revision, result.old_revid)
799
 
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
800
 
            source_refs=refs, overwrite=overwrite)
 
607
        self.update_tags(refs)
801
608
        result.new_revid = self.target.last_revision()
802
609
        return result
803
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
 
804
616
    def update_refs(self, stop_revision=None):
805
 
        interrepo = _mod_repository.InterRepository.get(self.source.repository,
 
617
        interrepo = repository.InterRepository.get(self.source.repository,
806
618
            self.target.repository)
807
619
        if stop_revision is None:
808
620
            refs = interrepo.fetch(branches=["HEAD"])
822
634
        result.old_revid = self.target.last_revision()
823
635
        refs, stop_revision = self.update_refs(stop_revision)
824
636
        self.target.generate_revision_history(stop_revision, result.old_revid)
825
 
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
826
 
            overwrite=overwrite, source_refs=refs)
 
637
        self.update_tags(refs)
827
638
        result.new_revid = self.target.last_revision()
828
639
        return result
829
640
 
830
641
 
831
642
class InterToGitBranch(branch.GenericInterBranch):
832
 
    """InterBranch implementation that pulls into a Git branch."""
 
643
    """InterBranch implementation that pulls from Git into bzr."""
833
644
 
834
645
    def __init__(self, source, target):
835
646
        super(InterToGitBranch, self).__init__(source, target)
836
 
        self.interrepo = _mod_repository.InterRepository.get(source.repository,
 
647
        self.interrepo = repository.InterRepository.get(source.repository,
837
648
                                           target.repository)
838
649
 
839
650
    @staticmethod
840
651
    def _get_branch_formats_to_test():
841
 
        try:
842
 
            default_format = branch.format_registry.get_default()
843
 
        except AttributeError:
844
 
            default_format = branch.BranchFormat._default_format
845
 
        return [(default_format, GitBranchFormat())]
 
652
        return []
846
653
 
847
654
    @classmethod
848
655
    def is_compatible(self, source, target):
849
656
        return (not isinstance(source, GitBranch) and
850
657
                isinstance(target, GitBranch))
851
658
 
 
659
    def update_revisions(self, *args, **kwargs):
 
660
        raise NoPushSupport()
 
661
 
852
662
    def _get_new_refs(self, stop_revision=None):
853
663
        if stop_revision is None:
854
 
            (stop_revno, stop_revision) = self.source.last_revision_info()
855
 
        else:
856
 
            stop_revno = self.source.revision_id_to_revno(stop_revision)
 
664
            stop_revision = self.source.last_revision()
857
665
        assert type(stop_revision) is str
858
666
        main_ref = self.target.ref or "refs/heads/master"
859
667
        refs = { main_ref: (None, stop_revision) }
860
668
        for name, revid in self.source.tags.get_tag_dict().iteritems():
861
669
            if self.source.repository.has_revision(revid):
862
670
                refs[tag_name_to_ref(name)] = (None, revid)
863
 
        return refs, main_ref, (stop_revno, stop_revision)
 
671
        return refs, main_ref
864
672
 
865
673
    def pull(self, overwrite=False, stop_revision=None, local=False,
866
 
             possible_transports=None, run_hooks=True):
 
674
             possible_transports=None):
 
675
        from dulwich.protocol import ZERO_SHA
867
676
        result = GitBranchPullResult()
868
677
        result.source_branch = self.source
869
678
        result.target_branch = self.target
870
 
        new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision)
 
679
        new_refs, main_ref = self._get_new_refs(stop_revision)
871
680
        def update_refs(old_refs):
 
681
            refs = dict(old_refs)
872
682
            # FIXME: Check for diverged branches
873
 
            return new_refs
874
 
        try:
875
 
            result.revidmap, old_refs, new_refs = self.interrepo.fetch_refs(
876
 
                update_refs, lossy=False)
877
 
        except NoPushSupport:
878
 
            raise errors.NoRoundtrippingSupport(self.source, self.target)
879
 
        (result.old_revid, old_sha1) = old_refs.get(main_ref, (ZERO_SHA, NULL_REVISION))
880
 
        if result.old_revid is None:
881
 
            result.old_revid = self.target.lookup_foreign_revision_id(old_sha1)
882
 
        result.new_revid = new_refs[main_ref][1]
 
683
            refs.update(new_refs)
 
684
            return refs
 
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]
883
689
        return result
884
690
 
885
 
    def push(self, overwrite=False, stop_revision=None, lossy=False,
 
691
    def push(self, overwrite=False, stop_revision=None,
886
692
             _override_hook_source_branch=None):
 
693
        from dulwich.protocol import ZERO_SHA
887
694
        result = GitBranchPushResult()
888
695
        result.source_branch = self.source
889
696
        result.target_branch = self.target
890
 
        new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision)
 
697
        new_refs, main_ref = self._get_new_refs(stop_revision)
891
698
        def update_refs(old_refs):
 
699
            refs = dict(old_refs)
892
700
            # FIXME: Check for diverged branches
893
 
            return new_refs
894
 
        try:
895
 
            result.revidmap, old_refs, new_refs = self.interrepo.fetch_refs(
896
 
                update_refs, lossy=lossy)
897
 
        except NoPushSupport:
898
 
            raise errors.NoRoundtrippingSupport(self.source, self.target)
 
701
            refs.update(new_refs)
 
702
            return refs
 
703
        old_refs, new_refs = self.interrepo.fetch_refs(update_refs)
899
704
        (result.old_revid, old_sha1) = old_refs.get(main_ref, (ZERO_SHA, NULL_REVISION))
900
705
        if result.old_revid is None:
901
706
            result.old_revid = self.target.lookup_foreign_revision_id(old_sha1)
902
 
        result.new_revid = new_refs[main_ref][1]
903
 
        (result.new_original_revno, result.new_original_revid) = stop_revinfo
 
707
        result.new_revid = new_refs[main_ref]
904
708
        return result
905
709
 
906
710
    def lossy_push(self, stop_revision=None):
907
 
        # For compatibility with bzr < 2.4
908
 
        return self.push(lossy=True, stop_revision=stop_revision)
909
 
 
910
 
 
911
 
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)
912
728
branch.InterBranch.register_optimiser(InterFromGitBranch)
913
729
branch.InterBranch.register_optimiser(InterToGitBranch)
914
 
branch.InterBranch.register_optimiser(InterLocalGitRemoteGitBranch)
 
730
branch.InterBranch.register_optimiser(InterGitLocalRemoteBranch)