/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

ImproveĀ docstrings.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2007 Canonical Ltd
2
 
# Copyright (C) 2009 Jelmer Vernooij <jelmer@samba.org>
 
2
# Copyright (C) 2009-2010 Jelmer Vernooij <jelmer@samba.org>
3
3
#
4
4
# This program is free software; you can redistribute it and/or modify
5
5
# it under the terms of the GNU General Public License as published by
27
27
    bzrdir,
28
28
    config,
29
29
    errors,
30
 
    foreign,
31
30
    repository,
32
31
    revision,
33
32
    tag,
41
40
    mutter,
42
41
    )
43
42
 
44
 
from bzrlib.plugins.git import (
45
 
    get_rich_root_format,
46
 
    )
47
43
from bzrlib.plugins.git.config import (
48
44
    GitBranchConfig,
49
45
    )
51
47
    NoPushSupport,
52
48
    NoSuchRef,
53
49
    )
54
 
 
55
 
try:
56
 
    from bzrlib.foreign import ForeignBranch
57
 
except ImportError:
58
 
    class ForeignBranch(branch.Branch):
59
 
        def __init__(self, mapping):
60
 
            self.mapping = mapping
61
 
            super(ForeignBranch, self).__init__()
62
 
 
63
 
 
64
 
def extract_tags(refs, mapping):
65
 
    ret = {}
66
 
    for k,v in refs.iteritems():
67
 
        if k.startswith("refs/tags/") and not k.endswith("^{}"):
68
 
            v = refs.get(k+"^{}", v)
69
 
            ret[k[len("refs/tags/"):]] = mapping.revision_id_foreign_to_bzr(v)
70
 
    return ret
 
50
from bzrlib.plugins.git.refs import (
 
51
    ref_to_branch_name,
 
52
    extract_tags,
 
53
    tag_name_to_ref,
 
54
    )
 
55
 
 
56
from bzrlib.foreign import ForeignBranch
71
57
 
72
58
 
73
59
class GitPullResult(branch.PullResult):
 
60
    """Result of a pull from a Git branch."""
74
61
 
75
62
    def _lookup_revno(self, revid):
76
63
        assert isinstance(revid, str), "was %r" % revid
95
82
 
96
83
    def get_tag_dict(self):
97
84
        ret = {}
98
 
        for k,v in self.repository._git.refs.as_dict("refs/tags").iteritems():
99
 
            obj = self.repository._git.get_object(v)
 
85
        for k,v in extract_tags(self.repository._git.get_refs()).iteritems():
 
86
            try:
 
87
                obj = self.repository._git[v]
 
88
            except KeyError:
 
89
                mutter("Tag %s points at unknown object %s, ignoring", v, obj)
 
90
                continue
100
91
            while isinstance(obj, Tag):
101
92
                v = obj.object[1]
102
 
                obj = self.repository._git.get_object(v)
 
93
                obj = self.repository._git[v]
103
94
            if not isinstance(obj, Commit):
104
95
                mutter("Tag %s points at object %r that is not a commit, "
105
96
                       "ignoring", k, obj)
106
97
                continue
107
 
            ret[k] = self.branch.mapping.revision_id_foreign_to_bzr(v)
 
98
            ret[k] = self.branch.lookup_foreign_revision_id(v)
108
99
        return ret
109
100
 
 
101
    def _set_tag_dict(self, to_dict):
 
102
        extra = set(self.repository._git.get_refs().keys())
 
103
        for k, revid in to_dict.iteritems():
 
104
            name = tag_name_to_ref(k)
 
105
            if name in extra:
 
106
                extra.remove(name)
 
107
            self.set_tag(k, revid)
 
108
        for name in extra:
 
109
            if name.startswith("refs/tags/"):
 
110
                del self.repository._git[name]
 
111
 
110
112
    def set_tag(self, name, revid):
111
 
        self.repository._git.refs["refs/tags/%s" % name], _ = \
 
113
        self.repository._git.refs[tag_name_to_ref(name)], _ = \
112
114
            self.branch.mapping.revision_id_bzr_to_foreign(revid)
113
115
 
114
116
 
 
117
class DictTagDict(LocalGitTagDict):
 
118
 
 
119
    def __init__(self, branch, tags):
 
120
        super(DictTagDict, self).__init__(branch)
 
121
        self._tags = tags
 
122
 
 
123
    def get_tag_dict(self):
 
124
        return self._tags
 
125
 
 
126
 
115
127
class GitBranchFormat(branch.BranchFormat):
116
128
 
117
129
    def get_format_description(self):
118
130
        return 'Git Branch'
119
131
 
 
132
    def network_name(self):
 
133
        return "git"
 
134
 
120
135
    def supports_tags(self):
121
136
        return True
122
137
 
 
138
    def get_foreign_tests_branch_factory(self):
 
139
        from bzrlib.plugins.git.tests.test_branch import ForeignTestsBranchFactory
 
140
        return ForeignTestsBranchFactory()
 
141
 
123
142
    def make_tags(self, branch):
124
143
        if getattr(branch.repository, "get_refs", None) is not None:
125
144
            from bzrlib.plugins.git.remote import RemoteGitTagDict
128
147
            return LocalGitTagDict(branch)
129
148
 
130
149
 
 
150
class GitReadLock(object):
 
151
 
 
152
    def __init__(self, unlock):
 
153
        self.unlock = unlock
 
154
 
 
155
 
 
156
class GitWriteLock(object):
 
157
 
 
158
    def __init__(self, unlock):
 
159
        self.unlock = unlock
 
160
 
 
161
 
131
162
class GitBranch(ForeignBranch):
132
163
    """An adapter to git repositories for bzr Branch objects."""
133
164
 
134
 
    def __init__(self, bzrdir, repository, name, lockfiles):
 
165
    def __init__(self, bzrdir, repository, ref, lockfiles, tagsdict=None):
135
166
        self.repository = repository
136
167
        self._format = GitBranchFormat()
137
168
        self.control_files = lockfiles
138
169
        self.bzrdir = bzrdir
139
170
        super(GitBranch, self).__init__(repository.get_mapping())
140
 
        self.name = name
 
171
        if tagsdict is not None:
 
172
            self.tags = DictTagDict(self, tagsdict)
 
173
        self.ref = ref
 
174
        self.name = ref_to_branch_name(ref)
141
175
        self._head = None
142
 
        self.base = bzrdir.transport.base
 
176
        self.base = bzrdir.root_transport.base
 
177
 
 
178
    def _get_checkout_format(self):
 
179
        """Return the most suitable metadir for a checkout of this branch.
 
180
        Weaves are used if this branch's repository uses weaves.
 
181
        """
 
182
        return bzrdir.format_registry.make_bzrdir("default")
 
183
 
 
184
    def get_child_submit_format(self):
 
185
        """Return the preferred format of submissions to this branch."""
 
186
        ret = self.get_config().get_user_option("child_submit_format")
 
187
        if ret is not None:
 
188
            return ret
 
189
        return "git"
143
190
 
144
191
    def _get_nick(self, local=False, possible_master_transports=None):
145
192
        """Find the nick name for this branch.
146
193
 
147
194
        :return: Branch nick
148
195
        """
149
 
        return self.name
 
196
        return self.name or "HEAD"
150
197
 
151
198
    def _set_nick(self, nick):
152
199
        raise NotImplementedError
154
201
    nick = property(_get_nick, _set_nick)
155
202
 
156
203
    def __repr__(self):
157
 
        return "%s(%r, %r)" % (self.__class__.__name__, self.repository.base, self.name)
 
204
        return "<%s(%r, %r)>" % (self.__class__.__name__, self.repository.base,
 
205
            self.ref or "HEAD")
158
206
 
159
207
    def generate_revision_history(self, revid, old_revid=None):
160
208
        # FIXME: Check that old_revid is in the ancestry of revid
163
211
 
164
212
    def lock_write(self):
165
213
        self.control_files.lock_write()
 
214
        return GitWriteLock(self.unlock)
166
215
 
167
216
    def get_stacked_on_url(self):
168
217
        # Git doesn't do stacking (yet...)
169
 
        return None
 
218
        raise errors.UnstackableBranchFormat(self._format, self.base)
170
219
 
171
220
    def get_parent(self):
172
221
        """See Branch.get_parent()."""
179
228
 
180
229
    def lock_read(self):
181
230
        self.control_files.lock_read()
 
231
        return GitReadLock(self.unlock)
182
232
 
183
233
    def is_locked(self):
184
234
        return self.control_files.is_locked()
194
244
        # perhaps should escape this ?
195
245
        if self.head is None:
196
246
            return revision.NULL_REVISION
197
 
        return self.mapping.revision_id_foreign_to_bzr(self.head)
 
247
        return self.lookup_foreign_revision_id(self.head)
198
248
 
199
249
    def _basic_push(self, target, overwrite=False, stop_revision=None):
200
250
        return branch.InterBranch.get(self, target)._basic_push(
201
251
            overwrite, stop_revision)
202
252
 
203
 
 
 
253
    def lookup_foreign_revision_id(self, foreign_revid):
 
254
        return self.repository.lookup_foreign_revision_id(foreign_revid,
 
255
            self.mapping)
 
256
 
 
257
 
204
258
class LocalGitBranch(GitBranch):
205
259
    """A local Git branch."""
206
260
 
207
 
    def _get_checkout_format(self):
208
 
        """Return the most suitable metadir for a checkout of this branch.
209
 
        Weaves are used if this branch's repository uses weaves.
210
 
        """
211
 
        format = self.repository.bzrdir.checkout_metadir()
212
 
        format.set_branch_format(self._format)
213
 
        return format
 
261
    def __init__(self, bzrdir, repository, name, lockfiles, tagsdict=None):
 
262
        super(LocalGitBranch, self).__init__(bzrdir, repository, name,
 
263
              lockfiles, tagsdict)
 
264
        refs = repository._git.get_refs()
 
265
        if not (name in refs.keys() or "HEAD" in refs.keys()):
 
266
            raise errors.NotBranchError(self.base)
214
267
 
215
268
    def create_checkout(self, to_location, revision_id=None, lightweight=False,
216
269
        accelerator_tree=None, hardlink=False):
219
272
            t.ensure_base()
220
273
            format = self._get_checkout_format()
221
274
            checkout = format.initialize_on_transport(t)
222
 
            from_branch = branch.BranchReferenceFormat().initialize(checkout, 
 
275
            from_branch = branch.BranchReferenceFormat().initialize(checkout,
223
276
                self)
224
277
            tree = checkout.create_workingtree(revision_id,
225
278
                from_branch=from_branch, hardlink=hardlink)
226
279
            return tree
227
280
        else:
228
281
            return self._create_heavyweight_checkout(to_location, revision_id,
229
 
            hardlink)
 
282
                hardlink)
230
283
 
231
 
    def _create_heavyweight_checkout(self, to_location, revision_id=None, 
 
284
    def _create_heavyweight_checkout(self, to_location, revision_id=None,
232
285
                                     hardlink=False):
233
286
        """Create a new heavyweight checkout of this branch.
234
287
 
238
291
        :return: WorkingTree object of checkout.
239
292
        """
240
293
        checkout_branch = bzrdir.BzrDir.create_branch_convenience(
241
 
            to_location, force_new_tree=False, format=get_rich_root_format())
 
294
            to_location, force_new_tree=False)
242
295
        checkout = checkout_branch.bzrdir
243
296
        checkout_branch.bind(self)
244
 
        # pull up to the specified revision_id to set the initial 
 
297
        # pull up to the specified revision_id to set the initial
245
298
        # branch tip correctly, and seed it with history.
246
299
        checkout_branch.pull(self, stop_revision=revision_id)
247
300
        return checkout.create_workingtree(revision_id, hardlink=hardlink)
256
309
 
257
310
    def _get_head(self):
258
311
        try:
259
 
            return self.repository._git.ref(self.name)
 
312
            return self.repository._git.ref(self.ref or "HEAD")
260
313
        except KeyError:
261
314
            return None
262
315
 
264
317
        self.set_last_revision(revid)
265
318
 
266
319
    def set_last_revision(self, revid):
267
 
        (newhead, self.mapping) = self.mapping.revision_id_bzr_to_foreign(
268
 
                revid)
 
320
        (newhead, self.mapping) = self.repository.lookup_bzr_revision_id(revid)
269
321
        self.head = newhead
270
322
 
271
323
    def _set_head(self, value):
272
324
        self._head = value
273
 
        self.repository._git.refs[self.name] = self._head
 
325
        self.repository._git.refs[self.ref or "HEAD"] = self._head
274
326
        self._clear_cached_state()
275
327
 
276
328
    head = property(_get_head, _set_head)
294
346
 
295
347
class GitBranchPullResult(branch.PullResult):
296
348
 
 
349
    def __init__(self):
 
350
        super(GitBranchPullResult, self).__init__()
 
351
        self.new_git_head = None
 
352
        self._old_revno = None
 
353
        self._new_revno = None
 
354
 
297
355
    def report(self, to_file):
298
356
        if not is_quiet():
299
357
            if self.old_revid == self.new_revid:
300
358
                to_file.write('No revisions to pull.\n')
301
 
            else:
302
 
                to_file.write('Now on revision %d (git sha: %s).\n' % 
 
359
            elif self.new_git_head is not None:
 
360
                to_file.write('Now on revision %d (git sha: %s).\n' %
303
361
                        (self.new_revno, self.new_git_head))
 
362
            else:
 
363
                to_file.write('Now on revision %d.\n' % (self.new_revno,))
304
364
        self._show_tag_conficts(to_file)
305
365
 
 
366
    def _lookup_revno(self, revid):
 
367
        assert isinstance(revid, str), "was %r" % revid
 
368
        # Try in source branch first, it'll be faster
 
369
        try:
 
370
            return self.source_branch.revision_id_to_revno(revid)
 
371
        except errors.NoSuchRevision:
 
372
            # FIXME: Check using graph.find_distance_to_null() ?
 
373
            return self.target_branch.revision_id_to_revno(revid)
 
374
 
 
375
    def _get_old_revno(self):
 
376
        if self._old_revno is not None:
 
377
            return self._old_revno
 
378
        return self._lookup_revno(self.old_revid)
 
379
 
 
380
    def _set_old_revno(self, revno):
 
381
        self._old_revno = revno
 
382
 
 
383
    old_revno = property(_get_old_revno, _set_old_revno)
 
384
 
 
385
    def _get_new_revno(self):
 
386
        if self._new_revno is not None:
 
387
            return self._new_revno
 
388
        return self._lookup_revno(self.new_revid)
 
389
 
 
390
    def _set_new_revno(self, revno):
 
391
        self._new_revno = revno
 
392
 
 
393
    new_revno = property(_get_new_revno, _set_new_revno)
 
394
 
306
395
 
307
396
class GitBranchPushResult(branch.BranchPushResult):
308
397
 
328
417
    """InterBranch implementation that pulls from Git into bzr."""
329
418
 
330
419
    @classmethod
331
 
    def is_compatible(self, source, target):
332
 
        return (isinstance(source, GitBranch) and 
333
 
                not isinstance(target, GitBranch))
334
 
 
335
 
    def update_revisions(self, stop_revision=None, overwrite=False,
336
 
        graph=None):
337
 
        """See InterBranch.update_revisions()."""
338
 
        interrepo = repository.InterRepository.get(self.source.repository, 
339
 
            self.target.repository)
340
 
        self._head = None
341
 
        self._last_revid = None
 
420
    def _get_interrepo(self, source, target):
 
421
        return repository.InterRepository.get(source.repository,
 
422
            target.repository)
 
423
 
 
424
    @classmethod
 
425
    def is_compatible(cls, source, target):
 
426
        return (isinstance(source, GitBranch) and
 
427
                not isinstance(target, GitBranch) and
 
428
                (getattr(cls._get_interrepo(source, target), "fetch_objects", None) is not None))
 
429
 
 
430
    def _update_revisions(self, stop_revision=None, overwrite=False,
 
431
        graph=None, limit=None):
 
432
        """Like InterBranch.update_revisions(), but with additions.
 
433
 
 
434
        Compared to the `update_revisions()` below, this function takes a
 
435
        `limit` argument that limits how many git commits will be converted
 
436
        and returns the new git head.
 
437
        """
 
438
        interrepo = self._get_interrepo(self.source, self.target)
342
439
        def determine_wants(heads):
343
 
            if not self.source.name in heads:
344
 
                raise NoSuchRef(self.source.name, heads.keys())
 
440
            if self.source.ref is not None and not self.source.ref in heads:
 
441
                raise NoSuchRef(self.source.ref, heads.keys())
345
442
            if stop_revision is not None:
346
443
                self._last_revid = stop_revision
347
 
                self._head, mapping = self.source.repository.lookup_git_revid(
 
444
                head, mapping = self.source.repository.lookup_bzr_revision_id(
348
445
                    stop_revision)
349
446
            else:
350
 
                self._head = heads[self.source.name]
351
 
                self._last_revid = \
352
 
                    self.source.mapping.revision_id_foreign_to_bzr(self._head)
 
447
                if self.source.ref is not None:
 
448
                    head = heads[self.source.ref]
 
449
                else:
 
450
                    head = heads["HEAD"]
 
451
                self._last_revid = self.source.lookup_foreign_revision_id(head)
353
452
            if self.target.repository.has_revision(self._last_revid):
354
453
                return []
355
 
            return [self._head]
356
 
        interrepo.fetch_objects(determine_wants, self.source.mapping)
 
454
            return [head]
 
455
        pack_hint, head = interrepo.fetch_objects(
 
456
            determine_wants, self.source.mapping, limit=limit)
 
457
        if (pack_hint is not None and
 
458
            self.target.repository._format.pack_compresses):
 
459
            self.target.repository.pack(hint=pack_hint)
 
460
        if head is not None:
 
461
            self._last_revid = self.source.lookup_foreign_revision_id(head)
357
462
        if overwrite:
358
463
            prev_last_revid = None
359
464
        else:
360
465
            prev_last_revid = self.target.last_revision()
361
 
        self.target.generate_revision_history(self._last_revid, prev_last_revid)
 
466
        self.target.generate_revision_history(self._last_revid,
 
467
            prev_last_revid)
 
468
        return head
 
469
 
 
470
    def update_revisions(self, stop_revision=None, overwrite=False,
 
471
                         graph=None):
 
472
        """See InterBranch.update_revisions()."""
 
473
        self._update_revisions(stop_revision, overwrite, graph)
362
474
 
363
475
    def pull(self, overwrite=False, stop_revision=None,
364
476
             possible_transports=None, _hook_master=None, run_hooks=True,
365
 
             _override_hook_target=None, local=False):
 
477
             _override_hook_target=None, local=False, limit=None):
366
478
        """See Branch.pull.
367
479
 
368
480
        :param _hook_master: Private parameter - set the branch to
372
484
            so it should not run its hooks.
373
485
        :param _override_hook_target: Private parameter - set the branch to be
374
486
            supplied as the target_branch to pull hooks.
 
487
        :param limit: Only import this many revisons.  `None`, the default,
 
488
            means import all revisions.
375
489
        """
376
490
        # This type of branch can't be bound.
377
491
        if local:
387
501
            # We assume that during 'pull' the target repository is closer than
388
502
            # the source one.
389
503
            graph = self.target.repository.get_graph(self.source.repository)
390
 
            result.old_revno, result.old_revid = \
 
504
            (result.old_revno, result.old_revid) = \
391
505
                self.target.last_revision_info()
392
 
            self.update_revisions(stop_revision, overwrite=overwrite, 
393
 
                graph=graph)
394
 
            result.new_git_head = self._head
 
506
            result.new_git_head = self._update_revisions(
 
507
                stop_revision, overwrite=overwrite, graph=graph, limit=limit)
395
508
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
396
509
                overwrite)
397
 
            result.new_revno, result.new_revid = self.target.last_revision_info()
 
510
            (result.new_revno, result.new_revid) = \
 
511
                self.target.last_revision_info()
398
512
            if _hook_master:
399
513
                result.master_branch = _hook_master
400
514
                result.local_branch = result.target_branch
414
528
        result.target_branch = self.target
415
529
        graph = self.target.repository.get_graph(self.source.repository)
416
530
        result.old_revno, result.old_revid = self.target.last_revision_info()
417
 
        self.update_revisions(stop_revision, overwrite=overwrite, 
418
 
            graph=graph)
419
 
        result.new_git_head = self._head
 
531
        result.new_git_head = self._update_revisions(
 
532
            stop_revision, overwrite=overwrite, graph=graph)
420
533
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
421
534
            overwrite)
422
535
        result.new_revno, result.new_revid = self.target.last_revision_info()
433
546
    @classmethod
434
547
    def is_compatible(self, source, target):
435
548
        from bzrlib.plugins.git.remote import RemoteGitBranch
436
 
        return (isinstance(source, LocalGitBranch) and 
 
549
        return (isinstance(source, LocalGitBranch) and
437
550
                isinstance(target, RemoteGitBranch))
438
551
 
439
552
    def _basic_push(self, overwrite=False, stop_revision=None):
 
553
        from dulwich.protocol import ZERO_SHA
440
554
        result = GitBranchPushResult()
441
555
        result.source_branch = self.source
442
556
        result.target_branch = self.target
444
558
            stop_revision = self.source.last_revision()
445
559
        # FIXME: Check for diverged branches
446
560
        def get_changed_refs(old_refs):
447
 
            result.old_revid = self.target.mapping.revision_id_foreign_to_bzr(old_refs.get("refs/heads/master", "0" * 40))
448
 
            refs = { "refs/heads/master": self.source.repository.lookup_git_revid(stop_revision)[0] }
 
561
            result.old_revid = self.target.lookup_foreign_revision_id(old_refs.get(self.target.ref, ZERO_SHA))
 
562
            refs = { self.target.ref: self.source.repository.lookup_bzr_revision_id(stop_revision)[0] }
449
563
            result.new_revid = stop_revision
450
564
            for name, sha in self.source.repository._git.refs.as_dict("refs/tags").iteritems():
451
 
                refs["refs/tags/%s" % name] = sha
 
565
                refs[tag_name_to_ref(name)] = sha
452
566
            return refs
453
 
        self.target.repository.send_pack(get_changed_refs, 
454
 
                self.source.repository._git.object_store.generate_pack_contents)
 
567
        self.target.repository.send_pack(get_changed_refs,
 
568
            self.source.repository._git.object_store.generate_pack_contents)
455
569
        return result
456
570
 
457
571
 
461
575
    @classmethod
462
576
    def is_compatible(self, source, target):
463
577
        from bzrlib.plugins.git.remote import RemoteGitBranch
464
 
        return (isinstance(source, RemoteGitBranch) and 
 
578
        return (isinstance(source, RemoteGitBranch) and
465
579
                isinstance(target, LocalGitBranch))
466
580
 
467
581
    def _basic_push(self, overwrite=False, stop_revision=None):
476
590
        return result
477
591
 
478
592
    def update_tags(self, refs):
479
 
        for name, revid in extract_tags(refs, self.target.mapping).iteritems():
 
593
        for name, v in extract_tags(refs).iteritems():
 
594
            revid = self.target.lookup_foreign_revision_id(v)
480
595
            self.target.tags.set_tag(name, revid)
481
596
 
482
597
    def update_refs(self, stop_revision=None):
483
 
        interrepo = repository.InterRepository.get(self.source.repository, 
 
598
        interrepo = repository.InterRepository.get(self.source.repository,
484
599
            self.target.repository)
485
600
        if stop_revision is None:
486
 
            refs = interrepo.fetch_refs(branches=["HEAD"])
487
 
            stop_revision = self.target.mapping.revision_id_foreign_to_bzr(refs["HEAD"])
 
601
            refs = interrepo.fetch(branches=["HEAD"])
 
602
            stop_revision = self.target.lookup_foreign_revision_id(refs["HEAD"])
488
603
        else:
489
 
            refs = interrepo.fetch_refs(revision_id=stop_revision)
 
604
            refs = interrepo.fetch(revision_id=stop_revision)
490
605
        return refs, stop_revision
491
606
 
492
 
    def pull(self, stop_revision=None, overwrite=False, 
493
 
        possible_transports=None, local=False):
 
607
    def pull(self, stop_revision=None, overwrite=False,
 
608
        possible_transports=None, run_hooks=True,local=False):
494
609
        # This type of branch can't be bound.
495
610
        if local:
496
611
            raise errors.LocalRequiresBoundBranch()
504
619
        result.new_revid = self.target.last_revision()
505
620
        return result
506
621
 
507
 
    
 
622
 
508
623
class InterToGitBranch(branch.InterBranch):
509
624
    """InterBranch implementation that pulls from Git into bzr."""
510
625
 
 
626
    def __init__(self, source, target):
 
627
        super(InterToGitBranch, self).__init__(source, target)
 
628
        self.interrepo = repository.InterRepository.get(source.repository,
 
629
                                           target.repository)
 
630
 
 
631
    @staticmethod
 
632
    def _get_branch_formats_to_test():
 
633
        return None, None
 
634
 
511
635
    @classmethod
512
636
    def is_compatible(self, source, target):
513
 
        return (not isinstance(source, GitBranch) and 
 
637
        return (not isinstance(source, GitBranch) and
514
638
                isinstance(target, GitBranch))
515
639
 
516
640
    def update_revisions(self, *args, **kwargs):
517
641
        raise NoPushSupport()
518
642
 
519
 
    def push(self, overwrite=True, stop_revision=None, 
 
643
    def _get_new_refs(self, stop_revision=None):
 
644
        if stop_revision is None:
 
645
            stop_revision = self.source.last_revision()
 
646
        main_ref = self.target.ref or "refs/heads/master"
 
647
        refs = { main_ref: stop_revision }
 
648
        for name, revid in self.source.tags.get_tag_dict().iteritems():
 
649
            if self.source.repository.has_revision(revid):
 
650
                refs[tag_name_to_ref(name)] = revid
 
651
        return refs, main_ref
 
652
 
 
653
    def pull(self, overwrite=False, stop_revision=None, local=False,
 
654
             possible_transports=None):
 
655
        from dulwich.protocol import ZERO_SHA
 
656
        result = GitBranchPullResult()
 
657
        result.source_branch = self.source
 
658
        result.target_branch = self.target
 
659
        new_refs, main_ref = self._get_new_refs(stop_revision)
 
660
        def update_refs(old_refs):
 
661
            refs = dict(old_refs)
 
662
            # FIXME: Check for diverged branches
 
663
            refs.update(new_refs)
 
664
            return refs
 
665
        old_refs, new_refs = self.interrepo.fetch_refs(update_refs)
 
666
        result.old_revid = self.target.lookup_foreign_revision_id(
 
667
            old_refs.get(main_ref, ZERO_SHA))
 
668
        result.new_revid = new_refs[main_ref]
 
669
        return result
 
670
 
 
671
    def push(self, overwrite=False, stop_revision=None,
520
672
             _override_hook_source_branch=None):
521
 
        raise NoPushSupport()
 
673
        from dulwich.protocol import ZERO_SHA
 
674
        result = GitBranchPushResult()
 
675
        result.source_branch = self.source
 
676
        result.target_branch = self.target
 
677
        new_refs, main_ref = self._get_new_refs(stop_revision)
 
678
        def update_refs(old_refs):
 
679
            refs = dict(old_refs)
 
680
            # FIXME: Check for diverged branches
 
681
            refs.update(new_refs)
 
682
            return refs
 
683
        old_refs, new_refs = self.interrepo.fetch_refs(update_refs)
 
684
        result.old_revid = self.target.lookup_foreign_revision_id(
 
685
            old_refs.get(main_ref, ZERO_SHA))
 
686
        result.new_revid = new_refs[main_ref]
 
687
        return result
522
688
 
523
689
    def lossy_push(self, stop_revision=None):
 
690
        from dulwich.protocol import ZERO_SHA
524
691
        result = GitBranchPushResult()
525
692
        result.source_branch = self.source
526
693
        result.target_branch = self.target
527
 
        result.old_revid = self.target.last_revision()
528
 
        if stop_revision is None:
529
 
            stop_revision = self.source.last_revision()
530
 
        # FIXME: Check for diverged branches
531
 
        refs = { "refs/heads/master": stop_revision }
532
 
        for name, revid in self.source.tags.get_tag_dict().iteritems():
533
 
            if self.source.repository.has_revision(revid):
534
 
                refs["refs/tags/%s" % name] = revid
535
 
        revidmap, new_refs = self.target.repository.dfetch_refs(
536
 
            self.source.repository, refs)
537
 
        if revidmap != {}:
538
 
            self.target.generate_revision_history(revidmap[stop_revision])
539
 
            result.new_revid = revidmap[stop_revision]
540
 
        else:
541
 
            result.new_revid = result.old_revid
542
 
        result.revidmap = revidmap
 
694
        new_refs, main_ref = self._get_new_refs(stop_revision)
 
695
        def update_refs(old_refs):
 
696
            refs = dict(old_refs)
 
697
            # FIXME: Check for diverged branches
 
698
            refs.update(new_refs)
 
699
            return refs
 
700
        result.revidmap, old_refs, new_refs = self.interrepo.dfetch_refs(
 
701
            update_refs)
 
702
        result.old_revid = self.target.lookup_foreign_revision_id(
 
703
            old_refs.get(self.target.ref, ZERO_SHA))
 
704
        result.new_revid = self.target.lookup_foreign_revision_id(
 
705
            new_refs[main_ref])
543
706
        return result
544
707
 
545
708