remote-bzr: add support for bzr repos

In bazaar, a repository can contain multiple branches, and previously we were supporting only one branch at a time. Now we fetch them all. Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Felipe Contreras committed Apr 30, 2013 at 20:10 UTC 95b0c60831148ca929e31b214ab3d29537ba20c9
2 files changed +138 -56
contrib/remote-helpers/git-remote-bzr
+102 -56
@@ -27,6 +27,7 @@ import bzrlib.generate_ids
27 import bzrlib.transport
28 import bzrlib.errors
29 import bzrlib.ui
30 +import bzrlib.urlutils
31
32 import sys
33 import os
@@ -250,12 +251,13 @@ def export_files(tree, files):
251
252 return final
253
253 -def export_branch(branch, name):
254 +def export_branch(repo, name):
255 global prefix
256
257 ref = '%s/heads/%s' % (prefix, name)
258 tip = marks.get_tip(name)
259
260 + branch = branches[name]
261 repo = branch.repository
262
263 branch.lock_read()
@@ -323,7 +325,7 @@ def export_branch(branch, name):
325
326 count += 1
327 if (count % 100 == 0):
326 - print "progress revision %s (%d/%d)" % (revid, count, len(revs))
328 + print "progress revision %s '%s' (%d/%d)" % (revid, name, count, len(revs))
329 print "#############################################################"
330
331 branch.unlock()
@@ -348,7 +350,7 @@ def export_tag(repo, name):
350 def do_import(parser):
351 global dirname
352
351 - branch = parser.repo
353 + repo = parser.repo
354 path = os.path.join(dirname, 'marks-git')
355
356 print "feature done"
@@ -362,10 +364,10 @@ def do_import(parser):
364 ref = parser[1]
365 if ref.startswith('refs/heads/'):
366 name = ref[len('refs/heads/'):]
365 - export_branch(branch, name)
367 + export_branch(repo, name)
368 if ref.startswith('refs/tags/'):
369 name = ref[len('refs/tags/'):]
368 - export_tag(branch, name)
370 + export_tag(repo, name)
371 parser.next()
372
373 print 'done'
@@ -551,8 +553,11 @@ def parse_commit(parser):
553 ref = parser[1]
554 parser.next()
555
554 - if ref != 'refs/heads/master':
555 - die("bzr doesn't support multiple branches; use 'master'")
556 + if ref.startswith('refs/heads/'):
557 + name = ref[len('refs/heads/'):]
558 + branch = branches[name]
559 + else:
560 + die('unknown ref')
561
562 commit_mark = parser.get_mark()
563 parser.next()
@@ -588,8 +593,6 @@ def parse_commit(parser):
593 path = c_style_unescape(path).decode('utf-8')
594 files[path] = f
595
591 - branch = parser.repo
592 -
596 committer, date, tz = committer
597 parents = [str(mark_to_rev(p)) for p in parents]
598 revid = bzrlib.generate_ids.gen_revision_id(committer, date)
@@ -621,9 +624,6 @@ def parse_reset(parser):
624 ref = parser[1]
625 parser.next()
626
624 - if ref != 'refs/heads/master':
625 - die("bzr doesn't support multiple branches; use 'master'")
626 -
627 # ugh
628 if parser.check('commit'):
629 parse_commit(parser)
@@ -636,7 +636,7 @@ def parse_reset(parser):
636 parsed_refs[ref] = mark_to_rev(from_mark)
637
638 def do_export(parser):
639 - global parsed_refs, dirname, peer
639 + global parsed_refs, dirname
640
641 parser.next()
642
@@ -654,23 +654,24 @@ def do_export(parser):
654 else:
655 die('unhandled export command: %s' % line)
656
657 - branch = parser.repo
658 -
657 for ref, revid in parsed_refs.iteritems():
660 - if ref == 'refs/heads/master':
661 - branch.generate_revision_history(revid, marks.get_tip('master'))
662 - if peer:
663 - try:
664 - branch.push(peer, stop_revision=revid)
665 - except bzrlib.errors.DivergedBranches:
666 - print "error %s non-fast forward" % ref
667 - continue
658 + name = ref[len('refs/heads/'):]
659 + branch = branches[name]
660 + branch.generate_revision_history(revid, marks.get_tip(name))
661
662 + if name in peers:
663 + peer = peers[name]
664 try:
670 - wt = branch.bzrdir.open_workingtree()
671 - wt.update()
672 - except bzrlib.errors.NoWorkingTree:
673 - pass
665 + peer.bzrdir.push_branch(branch, revision_id=revid)
666 + except bzrlib.errors.DivergedBranches:
667 + print "error %s non-fast forward" % ref
668 + continue
669 +
670 + try:
671 + wt = branch.bzrdir.open_workingtree()
672 + wt.update()
673 + except bzrlib.errors.NoWorkingTree:
674 + pass
675
676 print "ok %s" % ref
677
@@ -697,9 +698,15 @@ def ref_is_valid(name):
698
699 def do_list(parser):
700 global tags
700 - print "? refs/heads/%s" % 'master'
701
702 - branch = parser.repo
702 + master_branch = None
703 +
704 + for name in branches:
705 + if not master_branch:
706 + master_branch = name
707 + print "? refs/heads/%s" % name
708 +
709 + branch = branches[master_branch]
710 branch.lock_read()
711 for tag, revid in branch.tags.get_tag_dict().items():
712 try:
@@ -711,41 +718,77 @@ def do_list(parser):
718 print "? refs/tags/%s" % tag
719 tags[tag] = revid
720 branch.unlock()
714 - print "@refs/heads/%s HEAD" % 'master'
721 +
722 + print "@refs/heads/%s HEAD" % master_branch
723 print
724
725 +def get_remote_branch(origin, remote_branch, name):
726 + global dirname, peers
727 +
728 + branch_path = os.path.join(dirname, 'clone', name)
729 + if os.path.exists(branch_path):
730 + # pull
731 + d = bzrlib.bzrdir.BzrDir.open(branch_path)
732 + branch = d.open_branch()
733 + try:
734 + branch.pull(remote_branch, [], None, False)
735 + except bzrlib.errors.DivergedBranches:
736 + # use remote branch for now
737 + return remote_branch
738 + else:
739 + # clone
740 + d = origin.sprout(branch_path, None,
741 + hardlink=True, create_tree_if_local=False,
742 + force_new_repo=False,
743 + source_branch=remote_branch)
744 + branch = d.open_branch()
745 +
746 + return branch
747 +
748 def get_repo(url, alias):
718 - global dirname, peer
749 + global dirname, peer, branches
750
751 + normal_url = bzrlib.urlutils.normalize_url(url)
752 origin = bzrlib.bzrdir.BzrDir.open(url)
721 - branch = origin.open_branch()
722 -
723 - if not isinstance(origin.transport, bzrlib.transport.local.LocalTransport):
724 - clone_path = os.path.join(dirname, 'clone')
725 - remote_branch = branch
726 - if os.path.exists(clone_path):
727 - # pull
728 - d = bzrlib.bzrdir.BzrDir.open(clone_path)
729 - branch = d.open_branch()
730 - try:
731 - result = branch.pull(remote_branch, [], None, False)
732 - except bzrlib.errors.DivergedBranches:
733 - # use remote branch for now
734 - peer = None
735 - return remote_branch
753 + is_local = isinstance(origin.transport, bzrlib.transport.local.LocalTransport)
754 +
755 + clone_path = os.path.join(dirname, 'clone')
756 +
757 + try:
758 + repo = origin.open_repository()
759 + except bzrlib.errors.NoRepositoryPresent:
760 + # branch
761 +
762 + name = 'master'
763 + branch = origin.open_branch()
764 +
765 + if not is_local:
766 + if not os.path.exists(clone_path):
767 + os.mkdir(clone_path)
768 + peers[name] = branch
769 + branches[name] = get_remote_branch(origin, branch, name)
770 else:
737 - # clone
738 - d = origin.sprout(clone_path, None,
739 - hardlink=True, create_tree_if_local=False,
740 - source_branch=remote_branch)
741 - branch = d.open_branch()
742 - branch.bind(remote_branch)
743 -
744 - peer = remote_branch
771 + branches[name] = branch
772 +
773 + return branch.repository
774 else:
746 - peer = None
775 + # repository
776
748 - return branch
777 + if not is_local and not os.path.exists(clone_path):
778 + clonedir = bzrlib.bzrdir.BzrDir.create(clone_path)
779 +
780 + for branch in repo.find_branches():
781 +
782 + name = repo.user_transport.relpath(branch.base)
783 + name = name if name != '' else 'master'
784 +
785 + if not is_local:
786 + peers[name] = branch
787 + branches[name] = get_remote_branch(origin, branch, name)
788 + else:
789 + branches[name] = branch
790 +
791 + return repo
792
793 def fix_path(alias, orig_url):
794 url = urlparse.urlparse(orig_url, 'file')
@@ -762,6 +805,7 @@ def main(args):
805 global parsed_refs
806 global files_cache
807 global is_tmp
808 + global branches, peers
809
810 alias = args[1]
811 url = args[2]
@@ -772,6 +816,8 @@ def main(args):
816 parsed_refs = {}
817 files_cache = {}
818 marks = None
819 + branches = {}
820 + peers = {}
821
822 if alias[5:] == url:
823 is_tmp = True
contrib/remote-helpers/test-bzr.sh
+36
@@ -264,4 +264,40 @@ test_expect_success 'pushing a merge' '
264 test_cmp expected actual
265 '
266
267 +cat > expected <<EOF
268 +origin/HEAD
269 +origin/branch
270 +origin/trunk
271 +EOF
272 +
273 +test_expect_success 'proper bzr repo' '
274 + mkdir -p tmp && cd tmp &&
275 + test_when_finished "cd .. && rm -rf tmp" &&
276 +
277 + bzr init-repo bzrrepo &&
278 +
279 + bzr init bzrrepo/trunk &&
280 + (
281 + cd bzrrepo/trunk &&
282 + echo one >> content &&
283 + bzr add content &&
284 + bzr commit -m one
285 + ) &&
286 +
287 + bzr branch bzrrepo/trunk bzrrepo/branch &&
288 + (
289 + cd bzrrepo/branch &&
290 + echo two >> content &&
291 + bzr commit -m one
292 + ) &&
293 +
294 + git clone "bzr::$PWD/bzrrepo" gitrepo &&
295 + (
296 + cd gitrepo &&
297 + git for-each-ref --format "%(refname:short)" refs/remotes/origin > ../actual
298 + ) &&
299 +
300 + test_cmp ../expected actual
301 +'
302 +
303 test_done