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