/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to branch.py

More work on roundtrip push support.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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], _ = \
112
 
            self.branch.mapping.revision_id_bzr_to_foreign(revid)
 
116
        self.repository._git.refs[tag_name_to_ref(name)], _ = \
 
117
            self.branch.lookup_bzr_revision_id(revid)
 
118
 
 
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
113
128
 
114
129
 
115
130
class GitBranchFormat(branch.BranchFormat):
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
    def lookup_bzr_revision_id(self, revid):
 
261
        return self.repository.lookup_bzr_revision_id(
 
262
            revid, mapping=self.mapping)
 
263
 
 
264
 
204
265
class LocalGitBranch(GitBranch):
205
266
    """A local Git branch."""
206
267
 
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
 
268
    def __init__(self, bzrdir, repository, name, lockfiles, tagsdict=None):
 
269
        super(LocalGitBranch, self).__init__(bzrdir, repository, name,
 
270
              lockfiles, tagsdict)
 
271
        refs = repository._git.get_refs()
 
272
        if not (name in refs.keys() or "HEAD" in refs.keys()):
 
273
            raise errors.NotBranchError(self.base)
214
274
 
215
275
    def create_checkout(self, to_location, revision_id=None, lightweight=False,
216
276
        accelerator_tree=None, hardlink=False):
219
279
            t.ensure_base()
220
280
            format = self._get_checkout_format()
221
281
            checkout = format.initialize_on_transport(t)
222
 
            from_branch = branch.BranchReferenceFormat().initialize(checkout, 
 
282
            from_branch = branch.BranchReferenceFormat().initialize(checkout,
223
283
                self)
224
284
            tree = checkout.create_workingtree(revision_id,
225
285
                from_branch=from_branch, hardlink=hardlink)
226
286
            return tree
227
287
        else:
228
288
            return self._create_heavyweight_checkout(to_location, revision_id,
229
 
            hardlink)
 
289
                hardlink)
230
290
 
231
 
    def _create_heavyweight_checkout(self, to_location, revision_id=None, 
 
291
    def _create_heavyweight_checkout(self, to_location, revision_id=None,
232
292
                                     hardlink=False):
233
293
        """Create a new heavyweight checkout of this branch.
234
294
 
238
298
        :return: WorkingTree object of checkout.
239
299
        """
240
300
        checkout_branch = bzrdir.BzrDir.create_branch_convenience(
241
 
            to_location, force_new_tree=False, format=get_rich_root_format())
 
301
            to_location, force_new_tree=False)
242
302
        checkout = checkout_branch.bzrdir
243
303
        checkout_branch.bind(self)
244
 
        # pull up to the specified revision_id to set the initial 
 
304
        # pull up to the specified revision_id to set the initial
245
305
        # branch tip correctly, and seed it with history.
246
306
        checkout_branch.pull(self, stop_revision=revision_id)
247
307
        return checkout.create_workingtree(revision_id, hardlink=hardlink)
256
316
 
257
317
    def _get_head(self):
258
318
        try:
259
 
            return self.repository._git.ref(self.name)
 
319
            return self.repository._git.ref(self.ref or "HEAD")
260
320
        except KeyError:
261
321
            return None
262
322
 
264
324
        self.set_last_revision(revid)
265
325
 
266
326
    def set_last_revision(self, revid):
267
 
        (newhead, self.mapping) = self.mapping.revision_id_bzr_to_foreign(
268
 
                revid)
 
327
        (newhead, self.mapping) = self.repository.lookup_bzr_revision_id(revid)
269
328
        self.head = newhead
270
329
 
271
330
    def _set_head(self, value):
272
331
        self._head = value
273
 
        self.repository._git.refs[self.name] = self._head
 
332
        self.repository._git.refs[self.ref or "HEAD"] = self._head
274
333
        self._clear_cached_state()
275
334
 
276
335
    head = property(_get_head, _set_head)
294
353
 
295
354
class GitBranchPullResult(branch.PullResult):
296
355
 
 
356
    def __init__(self):
 
357
        super(GitBranchPullResult, self).__init__()
 
358
        self.new_git_head = None
 
359
        self._old_revno = None
 
360
        self._new_revno = None
 
361
 
297
362
    def report(self, to_file):
298
363
        if not is_quiet():
299
364
            if self.old_revid == self.new_revid:
300
365
                to_file.write('No revisions to pull.\n')
301
 
            else:
302
 
                to_file.write('Now on revision %d (git sha: %s).\n' % 
 
366
            elif self.new_git_head is not None:
 
367
                to_file.write('Now on revision %d (git sha: %s).\n' %
303
368
                        (self.new_revno, self.new_git_head))
 
369
            else:
 
370
                to_file.write('Now on revision %d.\n' % (self.new_revno,))
304
371
        self._show_tag_conficts(to_file)
305
372
 
 
373
    def _lookup_revno(self, 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)
 
381
 
 
382
    def _get_old_revno(self):
 
383
        if self._old_revno is not None:
 
384
            return self._old_revno
 
385
        return self._lookup_revno(self.old_revid)
 
386
 
 
387
    def _set_old_revno(self, revno):
 
388
        self._old_revno = revno
 
389
 
 
390
    old_revno = property(_get_old_revno, _set_old_revno)
 
391
 
 
392
    def _get_new_revno(self):
 
393
        if self._new_revno is not None:
 
394
            return self._new_revno
 
395
        return self._lookup_revno(self.new_revid)
 
396
 
 
397
    def _set_new_revno(self, revno):
 
398
        self._new_revno = revno
 
399
 
 
400
    new_revno = property(_get_new_revno, _set_new_revno)
 
401
 
306
402
 
307
403
class GitBranchPushResult(branch.BranchPushResult):
308
404
 
327
423
class InterFromGitBranch(branch.GenericInterBranch):
328
424
    """InterBranch implementation that pulls from Git into bzr."""
329
425
 
330
 
    @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
 
426
    @staticmethod
 
427
    def _get_branch_formats_to_test():
 
428
        return []
 
429
 
 
430
    @classmethod
 
431
    def _get_interrepo(self, source, target):
 
432
        return repository.InterRepository.get(source.repository,
 
433
            target.repository)
 
434
 
 
435
    @classmethod
 
436
    def is_compatible(cls, source, target):
 
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
        """
 
449
        interrepo = self._get_interrepo(self.source, self.target)
342
450
        def determine_wants(heads):
343
 
            if not self.source.name in heads:
344
 
                raise NoSuchRef(self.source.name, heads.keys())
 
451
            if self.source.ref is not None and not self.source.ref in heads:
 
452
                raise NoSuchRef(self.source.ref, heads.keys())
345
453
            if stop_revision is not None:
346
454
                self._last_revid = stop_revision
347
 
                self._head, mapping = self.source.repository.lookup_git_revid(
 
455
                head, mapping = self.source.repository.lookup_bzr_revision_id(
348
456
                    stop_revision)
349
457
            else:
350
 
                self._head = heads[self.source.name]
351
 
                self._last_revid = \
352
 
                    self.source.mapping.revision_id_foreign_to_bzr(self._head)
 
458
                if self.source.ref is not None:
 
459
                    head = heads[self.source.ref]
 
460
                else:
 
461
                    head = heads["HEAD"]
 
462
                self._last_revid = self.source.lookup_foreign_revision_id(head)
353
463
            if self.target.repository.has_revision(self._last_revid):
354
464
                return []
355
 
            return [self._head]
356
 
        interrepo.fetch_objects(determine_wants, self.source.mapping)
 
465
            return [head]
 
466
        pack_hint, head, refs = interrepo.fetch_objects(
 
467
            determine_wants, self.source.mapping, limit=limit)
 
468
        if (pack_hint is not None and
 
469
            self.target.repository._format.pack_compresses):
 
470
            self.target.repository.pack(hint=pack_hint)
 
471
        if head is not None:
 
472
            self._last_revid = self.source.lookup_foreign_revision_id(head)
357
473
        if overwrite:
358
474
            prev_last_revid = None
359
475
        else:
360
476
            prev_last_revid = self.target.last_revision()
361
 
        self.target.generate_revision_history(self._last_revid, prev_last_revid)
 
477
        self.target.generate_revision_history(self._last_revid,
 
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)
362
485
 
363
486
    def pull(self, overwrite=False, stop_revision=None,
364
487
             possible_transports=None, _hook_master=None, run_hooks=True,
365
 
             _override_hook_target=None, local=False):
 
488
             _override_hook_target=None, local=False, limit=None):
366
489
        """See Branch.pull.
367
490
 
368
491
        :param _hook_master: Private parameter - set the branch to
372
495
            so it should not run its hooks.
373
496
        :param _override_hook_target: Private parameter - set the branch to be
374
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.
375
500
        """
376
501
        # This type of branch can't be bound.
377
502
        if local:
387
512
            # We assume that during 'pull' the target repository is closer than
388
513
            # the source one.
389
514
            graph = self.target.repository.get_graph(self.source.repository)
390
 
            result.old_revno, result.old_revid = \
 
515
            (result.old_revno, result.old_revid) = \
391
516
                self.target.last_revision_info()
392
 
            self.update_revisions(stop_revision, overwrite=overwrite, 
393
 
                graph=graph)
394
 
            result.new_git_head = self._head
 
517
            result.new_git_head = self._update_revisions(
 
518
                stop_revision, overwrite=overwrite, graph=graph, limit=limit)
395
519
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
396
520
                overwrite)
397
 
            result.new_revno, result.new_revid = self.target.last_revision_info()
 
521
            (result.new_revno, result.new_revid) = \
 
522
                self.target.last_revision_info()
398
523
            if _hook_master:
399
524
                result.master_branch = _hook_master
400
525
                result.local_branch = result.target_branch
414
539
        result.target_branch = self.target
415
540
        graph = self.target.repository.get_graph(self.source.repository)
416
541
        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
 
542
        result.new_git_head = self._update_revisions(
 
543
            stop_revision, overwrite=overwrite, graph=graph)
420
544
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
421
545
            overwrite)
422
546
        result.new_revno, result.new_revid = self.target.last_revision_info()
430
554
class InterGitLocalRemoteBranch(InterGitBranch):
431
555
    """InterBranch that copies from a local to a remote git branch."""
432
556
 
 
557
    @staticmethod
 
558
    def _get_branch_formats_to_test():
 
559
        return []
 
560
 
433
561
    @classmethod
434
562
    def is_compatible(self, source, target):
435
563
        from bzrlib.plugins.git.remote import RemoteGitBranch
436
 
        return (isinstance(source, LocalGitBranch) and 
 
564
        return (isinstance(source, LocalGitBranch) and
437
565
                isinstance(target, RemoteGitBranch))
438
566
 
439
567
    def _basic_push(self, overwrite=False, stop_revision=None):
 
568
        from dulwich.protocol import ZERO_SHA
440
569
        result = GitBranchPushResult()
441
570
        result.source_branch = self.source
442
571
        result.target_branch = self.target
444
573
            stop_revision = self.source.last_revision()
445
574
        # FIXME: Check for diverged branches
446
575
        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] }
 
576
            result.old_revid = self.target.lookup_foreign_revision_id(old_refs.get(self.target.ref, ZERO_SHA))
 
577
            refs = { self.target.ref: self.source.repository.lookup_bzr_revision_id(stop_revision)[0] }
449
578
            result.new_revid = stop_revision
450
579
            for name, sha in self.source.repository._git.refs.as_dict("refs/tags").iteritems():
451
 
                refs["refs/tags/%s" % name] = sha
 
580
                refs[tag_name_to_ref(name)] = sha
452
581
            return refs
453
 
        self.target.repository.send_pack(get_changed_refs, 
454
 
                self.source.repository._git.object_store.generate_pack_contents)
 
582
        self.target.repository.send_pack(get_changed_refs,
 
583
            self.source.repository._git.object_store.generate_pack_contents)
455
584
        return result
456
585
 
457
586
 
458
587
class InterGitRemoteLocalBranch(InterGitBranch):
459
588
    """InterBranch that copies from a remote to a local git branch."""
460
589
 
 
590
    @staticmethod
 
591
    def _get_branch_formats_to_test():
 
592
        return []
 
593
 
461
594
    @classmethod
462
595
    def is_compatible(self, source, target):
463
596
        from bzrlib.plugins.git.remote import RemoteGitBranch
464
 
        return (isinstance(source, RemoteGitBranch) and 
 
597
        return (isinstance(source, RemoteGitBranch) and
465
598
                isinstance(target, LocalGitBranch))
466
599
 
467
600
    def _basic_push(self, overwrite=False, stop_revision=None):
476
609
        return result
477
610
 
478
611
    def update_tags(self, refs):
479
 
        for name, revid in extract_tags(refs, self.target.mapping).iteritems():
 
612
        for name, v in extract_tags(refs).iteritems():
 
613
            revid = self.target.lookup_foreign_revision_id(v)
480
614
            self.target.tags.set_tag(name, revid)
481
615
 
482
616
    def update_refs(self, stop_revision=None):
483
 
        interrepo = repository.InterRepository.get(self.source.repository, 
 
617
        interrepo = repository.InterRepository.get(self.source.repository,
484
618
            self.target.repository)
485
619
        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"])
 
620
            refs = interrepo.fetch(branches=["HEAD"])
 
621
            stop_revision = self.target.lookup_foreign_revision_id(refs["HEAD"])
488
622
        else:
489
 
            refs = interrepo.fetch_refs(revision_id=stop_revision)
 
623
            refs = interrepo.fetch(revision_id=stop_revision)
490
624
        return refs, stop_revision
491
625
 
492
 
    def pull(self, stop_revision=None, overwrite=False, 
493
 
        possible_transports=None, local=False):
 
626
    def pull(self, stop_revision=None, overwrite=False,
 
627
        possible_transports=None, run_hooks=True,local=False):
494
628
        # This type of branch can't be bound.
495
629
        if local:
496
630
            raise errors.LocalRequiresBoundBranch()
504
638
        result.new_revid = self.target.last_revision()
505
639
        return result
506
640
 
507
 
    
508
 
class InterToGitBranch(branch.InterBranch):
 
641
 
 
642
class InterToGitBranch(branch.GenericInterBranch):
509
643
    """InterBranch implementation that pulls from Git into bzr."""
510
644
 
 
645
    def __init__(self, source, target):
 
646
        super(InterToGitBranch, self).__init__(source, target)
 
647
        self.interrepo = repository.InterRepository.get(source.repository,
 
648
                                           target.repository)
 
649
 
 
650
    @staticmethod
 
651
    def _get_branch_formats_to_test():
 
652
        return []
 
653
 
511
654
    @classmethod
512
655
    def is_compatible(self, source, target):
513
 
        return (not isinstance(source, GitBranch) and 
 
656
        return (not isinstance(source, GitBranch) and
514
657
                isinstance(target, GitBranch))
515
658
 
516
659
    def update_revisions(self, *args, **kwargs):
517
660
        raise NoPushSupport()
518
661
 
519
 
    def push(self, overwrite=True, stop_revision=None, 
 
662
    def _get_new_refs(self, stop_revision=None):
 
663
        if stop_revision is None:
 
664
            stop_revision = self.source.last_revision()
 
665
        assert type(stop_revision) is str
 
666
        main_ref = self.target.ref or "refs/heads/master"
 
667
        refs = { main_ref: (None, stop_revision) }
 
668
        for name, revid in self.source.tags.get_tag_dict().iteritems():
 
669
            if self.source.repository.has_revision(revid):
 
670
                refs[tag_name_to_ref(name)] = (None, revid)
 
671
        return refs, main_ref
 
672
 
 
673
    def pull(self, overwrite=False, stop_revision=None, local=False,
 
674
             possible_transports=None):
 
675
        from dulwich.protocol import ZERO_SHA
 
676
        result = GitBranchPullResult()
 
677
        result.source_branch = self.source
 
678
        result.target_branch = self.target
 
679
        new_refs, main_ref = self._get_new_refs(stop_revision)
 
680
        def update_refs(old_refs):
 
681
            refs = dict(old_refs)
 
682
            # FIXME: Check for diverged branches
 
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]
 
689
        return result
 
690
 
 
691
    def push(self, overwrite=False, stop_revision=None,
520
692
             _override_hook_source_branch=None):
521
 
        raise NoPushSupport()
 
693
        from dulwich.protocol import ZERO_SHA
 
694
        result = GitBranchPushResult()
 
695
        result.source_branch = self.source
 
696
        result.target_branch = self.target
 
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
        old_refs, new_refs = self.interrepo.fetch_refs(update_refs)
 
704
        (result.old_revid, old_sha1) = old_refs.get(main_ref, (ZERO_SHA, NULL_REVISION))
 
705
        if result.old_revid is None:
 
706
            result.old_revid = self.target.lookup_foreign_revision_id(old_sha1)
 
707
        result.new_revid = new_refs[main_ref]
 
708
        return result
522
709
 
523
710
    def lossy_push(self, stop_revision=None):
524
711
        result = GitBranchPushResult()
525
712
        result.source_branch = self.source
526
713
        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
 
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]
543
724
        return result
544
725
 
545
726