/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

Fix dpush to remote locations.

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