/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

Initial work on supporting move_in_thin_pack.

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
 
327
419
class InterFromGitBranch(branch.GenericInterBranch):
328
420
    """InterBranch implementation that pulls from Git into bzr."""
329
421
 
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
 
422
    @staticmethod
 
423
    def _get_branch_formats_to_test():
 
424
        return []
 
425
 
 
426
    @classmethod
 
427
    def _get_interrepo(self, source, target):
 
428
        return repository.InterRepository.get(source.repository,
 
429
            target.repository)
 
430
 
 
431
    @classmethod
 
432
    def is_compatible(cls, source, target):
 
433
        return (isinstance(source, GitBranch) and
 
434
                not isinstance(target, GitBranch) and
 
435
                (getattr(cls._get_interrepo(source, target), "fetch_objects", None) is not None))
 
436
 
 
437
    def _update_revisions(self, stop_revision=None, overwrite=False,
 
438
        graph=None, limit=None):
 
439
        """Like InterBranch.update_revisions(), but with additions.
 
440
 
 
441
        Compared to the `update_revisions()` below, this function takes a
 
442
        `limit` argument that limits how many git commits will be converted
 
443
        and returns the new git head.
 
444
        """
 
445
        interrepo = self._get_interrepo(self.source, self.target)
342
446
        def determine_wants(heads):
343
 
            if not self.source.name in heads:
344
 
                raise NoSuchRef(self.source.name, heads.keys())
 
447
            if self.source.ref is not None and not self.source.ref in heads:
 
448
                raise NoSuchRef(self.source.ref, heads.keys())
345
449
            if stop_revision is not None:
346
450
                self._last_revid = stop_revision
347
 
                self._head, mapping = self.source.repository.lookup_git_revid(
 
451
                head, mapping = self.source.repository.lookup_bzr_revision_id(
348
452
                    stop_revision)
349
453
            else:
350
 
                self._head = heads[self.source.name]
351
 
                self._last_revid = \
352
 
                    self.source.mapping.revision_id_foreign_to_bzr(self._head)
 
454
                if self.source.ref is not None:
 
455
                    head = heads[self.source.ref]
 
456
                else:
 
457
                    head = heads["HEAD"]
 
458
                self._last_revid = self.source.lookup_foreign_revision_id(head)
353
459
            if self.target.repository.has_revision(self._last_revid):
354
460
                return []
355
 
            return [self._head]
356
 
        interrepo.fetch_objects(determine_wants, self.source.mapping)
 
461
            return [head]
 
462
        pack_hint, head, refs = interrepo.fetch_objects(
 
463
            determine_wants, self.source.mapping, limit=limit)
 
464
        if (pack_hint is not None and
 
465
            self.target.repository._format.pack_compresses):
 
466
            self.target.repository.pack(hint=pack_hint)
 
467
        if head is not None:
 
468
            self._last_revid = self.source.lookup_foreign_revision_id(head)
357
469
        if overwrite:
358
470
            prev_last_revid = None
359
471
        else:
360
472
            prev_last_revid = self.target.last_revision()
361
 
        self.target.generate_revision_history(self._last_revid, prev_last_revid)
 
473
        self.target.generate_revision_history(self._last_revid,
 
474
            prev_last_revid)
 
475
        return head
 
476
 
 
477
    def update_revisions(self, stop_revision=None, overwrite=False,
 
478
                         graph=None):
 
479
        """See InterBranch.update_revisions()."""
 
480
        self._update_revisions(stop_revision, overwrite, graph)
362
481
 
363
482
    def pull(self, overwrite=False, stop_revision=None,
364
483
             possible_transports=None, _hook_master=None, run_hooks=True,
365
 
             _override_hook_target=None, local=False):
 
484
             _override_hook_target=None, local=False, limit=None):
366
485
        """See Branch.pull.
367
486
 
368
487
        :param _hook_master: Private parameter - set the branch to
372
491
            so it should not run its hooks.
373
492
        :param _override_hook_target: Private parameter - set the branch to be
374
493
            supplied as the target_branch to pull hooks.
 
494
        :param limit: Only import this many revisons.  `None`, the default,
 
495
            means import all revisions.
375
496
        """
376
497
        # This type of branch can't be bound.
377
498
        if local:
387
508
            # We assume that during 'pull' the target repository is closer than
388
509
            # the source one.
389
510
            graph = self.target.repository.get_graph(self.source.repository)
390
 
            result.old_revno, result.old_revid = \
 
511
            (result.old_revno, result.old_revid) = \
391
512
                self.target.last_revision_info()
392
 
            self.update_revisions(stop_revision, overwrite=overwrite, 
393
 
                graph=graph)
394
 
            result.new_git_head = self._head
 
513
            result.new_git_head = self._update_revisions(
 
514
                stop_revision, overwrite=overwrite, graph=graph, limit=limit)
395
515
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
396
516
                overwrite)
397
 
            result.new_revno, result.new_revid = self.target.last_revision_info()
 
517
            (result.new_revno, result.new_revid) = \
 
518
                self.target.last_revision_info()
398
519
            if _hook_master:
399
520
                result.master_branch = _hook_master
400
521
                result.local_branch = result.target_branch
414
535
        result.target_branch = self.target
415
536
        graph = self.target.repository.get_graph(self.source.repository)
416
537
        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
 
538
        result.new_git_head = self._update_revisions(
 
539
            stop_revision, overwrite=overwrite, graph=graph)
420
540
        result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
421
541
            overwrite)
422
542
        result.new_revno, result.new_revid = self.target.last_revision_info()
430
550
class InterGitLocalRemoteBranch(InterGitBranch):
431
551
    """InterBranch that copies from a local to a remote git branch."""
432
552
 
 
553
    @staticmethod
 
554
    def _get_branch_formats_to_test():
 
555
        return []
 
556
 
433
557
    @classmethod
434
558
    def is_compatible(self, source, target):
435
559
        from bzrlib.plugins.git.remote import RemoteGitBranch
436
 
        return (isinstance(source, LocalGitBranch) and 
 
560
        return (isinstance(source, LocalGitBranch) and
437
561
                isinstance(target, RemoteGitBranch))
438
562
 
439
563
    def _basic_push(self, overwrite=False, stop_revision=None):
 
564
        from dulwich.protocol import ZERO_SHA
440
565
        result = GitBranchPushResult()
441
566
        result.source_branch = self.source
442
567
        result.target_branch = self.target
444
569
            stop_revision = self.source.last_revision()
445
570
        # FIXME: Check for diverged branches
446
571
        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] }
 
572
            result.old_revid = self.target.lookup_foreign_revision_id(old_refs.get(self.target.ref, ZERO_SHA))
 
573
            refs = { self.target.ref: self.source.repository.lookup_bzr_revision_id(stop_revision)[0] }
449
574
            result.new_revid = stop_revision
450
575
            for name, sha in self.source.repository._git.refs.as_dict("refs/tags").iteritems():
451
 
                refs["refs/tags/%s" % name] = sha
 
576
                refs[tag_name_to_ref(name)] = sha
452
577
            return refs
453
 
        self.target.repository.send_pack(get_changed_refs, 
454
 
                self.source.repository._git.object_store.generate_pack_contents)
 
578
        self.target.repository.send_pack(get_changed_refs,
 
579
            self.source.repository._git.object_store.generate_pack_contents)
455
580
        return result
456
581
 
457
582
 
458
583
class InterGitRemoteLocalBranch(InterGitBranch):
459
584
    """InterBranch that copies from a remote to a local git branch."""
460
585
 
 
586
    @staticmethod
 
587
    def _get_branch_formats_to_test():
 
588
        return []
 
589
 
461
590
    @classmethod
462
591
    def is_compatible(self, source, target):
463
592
        from bzrlib.plugins.git.remote import RemoteGitBranch
464
 
        return (isinstance(source, RemoteGitBranch) and 
 
593
        return (isinstance(source, RemoteGitBranch) and
465
594
                isinstance(target, LocalGitBranch))
466
595
 
467
596
    def _basic_push(self, overwrite=False, stop_revision=None):
476
605
        return result
477
606
 
478
607
    def update_tags(self, refs):
479
 
        for name, revid in extract_tags(refs, self.target.mapping).iteritems():
 
608
        for name, v in extract_tags(refs).iteritems():
 
609
            revid = self.target.lookup_foreign_revision_id(v)
480
610
            self.target.tags.set_tag(name, revid)
481
611
 
482
612
    def update_refs(self, stop_revision=None):
483
 
        interrepo = repository.InterRepository.get(self.source.repository, 
 
613
        interrepo = repository.InterRepository.get(self.source.repository,
484
614
            self.target.repository)
485
615
        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"])
 
616
            refs = interrepo.fetch(branches=["HEAD"])
 
617
            stop_revision = self.target.lookup_foreign_revision_id(refs["HEAD"])
488
618
        else:
489
 
            refs = interrepo.fetch_refs(revision_id=stop_revision)
 
619
            refs = interrepo.fetch(revision_id=stop_revision)
490
620
        return refs, stop_revision
491
621
 
492
 
    def pull(self, stop_revision=None, overwrite=False, 
493
 
        possible_transports=None, local=False):
 
622
    def pull(self, stop_revision=None, overwrite=False,
 
623
        possible_transports=None, run_hooks=True,local=False):
494
624
        # This type of branch can't be bound.
495
625
        if local:
496
626
            raise errors.LocalRequiresBoundBranch()
504
634
        result.new_revid = self.target.last_revision()
505
635
        return result
506
636
 
507
 
    
508
 
class InterToGitBranch(branch.InterBranch):
 
637
 
 
638
class InterToGitBranch(branch.GenericInterBranch):
509
639
    """InterBranch implementation that pulls from Git into bzr."""
510
640
 
 
641
    def __init__(self, source, target):
 
642
        super(InterToGitBranch, self).__init__(source, target)
 
643
        self.interrepo = repository.InterRepository.get(source.repository,
 
644
                                           target.repository)
 
645
 
 
646
    @staticmethod
 
647
    def _get_branch_formats_to_test():
 
648
        return []
 
649
 
511
650
    @classmethod
512
651
    def is_compatible(self, source, target):
513
 
        return (not isinstance(source, GitBranch) and 
 
652
        return (not isinstance(source, GitBranch) and
514
653
                isinstance(target, GitBranch))
515
654
 
516
655
    def update_revisions(self, *args, **kwargs):
517
656
        raise NoPushSupport()
518
657
 
519
 
    def push(self, overwrite=True, stop_revision=None, 
 
658
    def _get_new_refs(self, stop_revision=None):
 
659
        if stop_revision is None:
 
660
            stop_revision = self.source.last_revision()
 
661
        assert type(stop_revision) is str
 
662
        main_ref = self.target.ref or "refs/heads/master"
 
663
        refs = { main_ref: (None, stop_revision) }
 
664
        for name, revid in self.source.tags.get_tag_dict().iteritems():
 
665
            if self.source.repository.has_revision(revid):
 
666
                refs[tag_name_to_ref(name)] = (None, revid)
 
667
        return refs, main_ref
 
668
 
 
669
    def pull(self, overwrite=False, stop_revision=None, local=False,
 
670
             possible_transports=None):
 
671
        from dulwich.protocol import ZERO_SHA
 
672
        result = GitBranchPullResult()
 
673
        result.source_branch = self.source
 
674
        result.target_branch = self.target
 
675
        new_refs, main_ref = self._get_new_refs(stop_revision)
 
676
        def update_refs(old_refs):
 
677
            refs = dict(old_refs)
 
678
            # FIXME: Check for diverged branches
 
679
            refs.update(new_refs)
 
680
            return refs
 
681
        old_refs, new_refs = self.interrepo.fetch_refs(update_refs)
 
682
        result.old_revid = self.target.lookup_foreign_revision_id(
 
683
            old_refs.get(main_ref, ZERO_SHA))
 
684
        result.new_revid = new_refs[main_ref]
 
685
        return result
 
686
 
 
687
    def push(self, overwrite=False, stop_revision=None,
520
688
             _override_hook_source_branch=None):
521
 
        raise NoPushSupport()
 
689
        from dulwich.protocol import ZERO_SHA
 
690
        result = GitBranchPushResult()
 
691
        result.source_branch = self.source
 
692
        result.target_branch = self.target
 
693
        new_refs, main_ref = self._get_new_refs(stop_revision)
 
694
        def update_refs(old_refs):
 
695
            refs = dict(old_refs)
 
696
            # FIXME: Check for diverged branches
 
697
            refs.update(new_refs)
 
698
            return refs
 
699
        old_refs, new_refs = self.interrepo.fetch_refs(update_refs)
 
700
        result.old_revid = self.target.lookup_foreign_revision_id(
 
701
            old_refs.get(main_ref, ZERO_SHA))
 
702
        result.new_revid = new_refs[main_ref]
 
703
        return result
522
704
 
523
705
    def lossy_push(self, stop_revision=None):
524
706
        result = GitBranchPushResult()
525
707
        result.source_branch = self.source
526
708
        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
 
709
        new_refs, main_ref = self._get_new_refs(stop_revision)
 
710
        def update_refs(old_refs):
 
711
            refs = dict(old_refs)
 
712
            # FIXME: Check for diverged branches
 
713
            refs.update(new_refs)
 
714
            return refs
 
715
        result.revidmap, old_refs, new_refs = self.interrepo.dfetch_refs(
 
716
            update_refs)
 
717
        result.old_revid = old_refs.get(self.target.ref, (None, NULL_REVISION))[1]
 
718
        result.new_revid = new_refs[main_ref][1]
543
719
        return result
544
720
 
545
721