remote-bzr: reuse bzrlib transports when possible

Pass a list of open bzrlib.transport.Transport objects to each bzrlib function that might create a transport. This enables bzrlib to reuse existing transports when possible, avoiding multiple concurrent connections to the same remote server. If the remote server is accessed via ssh, this fixes a couple of problems: * If the user does not have keys loaded into an ssh agent, the user may be prompted for a password multiple times. * If the user is using OpenSSH and the ControlMaster setting is set to auto, git-remote-bzr might hang. This is because bzrlib closes the multiple ssh sessions in an undefined order and might try to close the master ssh session before the other sessions. The master ssh process will not exit until the other sessions have exited, causing a deadlock. (The ssh sessions are closed in an undefined order because bzrlib relies on the Python garbage collector to trigger ssh session termination.) Signed-off-by: Richard Hansen <rhansen@bbn.com> Acked-by: Felipe Contreras <felipe.contreras@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Richard Hansen committed Sep 8, 2013 at 01:47 UTC bd5424f0d6257b55f7269ee5f009c5ce5eb71c6e
1 file changed +21 -12
contrib/remote-helpers/git-remote-bzr
+21 -12
@@ -674,7 +674,7 @@ def parse_reset(parser):
674 parsed_refs[ref] = mark_to_rev(from_mark)
675
676 def do_export(parser):
677 - global parsed_refs, dirname
677 + global parsed_refs, dirname, transports
678
679 parser.next()
680
@@ -699,7 +699,8 @@ def do_export(parser):
699 branch.generate_revision_history(revid, marks.get_tip(name))
700
701 if name in peers:
702 - peer = bzrlib.branch.Branch.open(peers[name])
702 + peer = bzrlib.branch.Branch.open(peers[name],
703 + possible_transports=transports)
704 try:
705 peer.bzrdir.push_branch(branch, revision_id=revid)
706 except bzrlib.errors.DivergedBranches:
@@ -769,25 +770,28 @@ def do_list(parser):
770 print
771
772 def clone(path, remote_branch):
773 + global transports
774 try:
773 - bdir = bzrlib.bzrdir.BzrDir.create(path)
775 + bdir = bzrlib.bzrdir.BzrDir.create(path, possible_transports=transports)
776 except bzrlib.errors.AlreadyControlDirError:
775 - bdir = bzrlib.bzrdir.BzrDir.open(path)
777 + bdir = bzrlib.bzrdir.BzrDir.open(path, possible_transports=transports)
778 repo = bdir.find_repository()
779 repo.fetch(remote_branch.repository)
780 return remote_branch.sprout(bdir, repository=repo)
781
782 def get_remote_branch(name):
781 - global dirname, branches
783 + global dirname, branches, transports
784
783 - remote_branch = bzrlib.branch.Branch.open(branches[name])
785 + remote_branch = bzrlib.branch.Branch.open(branches[name],
786 + possible_transports=transports)
787 if isinstance(remote_branch.user_transport, bzrlib.transport.local.LocalTransport):
788 return remote_branch
789
790 branch_path = os.path.join(dirname, 'clone', name)
791
792 try:
790 - branch = bzrlib.branch.Branch.open(branch_path)
793 + branch = bzrlib.branch.Branch.open(branch_path,
794 + possible_transports=transports)
795 except bzrlib.errors.NotBranchError:
796 # clone
797 branch = clone(branch_path, remote_branch)
@@ -821,17 +825,19 @@ def find_branches(repo):
825 yield name, branch.base
826
827 def get_repo(url, alias):
824 - global dirname, peer, branches
828 + global dirname, peer, branches, transports
829
830 normal_url = bzrlib.urlutils.normalize_url(url)
827 - origin = bzrlib.bzrdir.BzrDir.open(url)
831 + origin = bzrlib.bzrdir.BzrDir.open(url, possible_transports=transports)
832 is_local = isinstance(origin.transport, bzrlib.transport.local.LocalTransport)
833
834 shared_path = os.path.join(gitdir, 'bzr')
835 try:
832 - shared_dir = bzrlib.bzrdir.BzrDir.open(shared_path)
836 + shared_dir = bzrlib.bzrdir.BzrDir.open(shared_path,
837 + possible_transports=transports)
838 except bzrlib.errors.NotBranchError:
834 - shared_dir = bzrlib.bzrdir.BzrDir.create(shared_path)
839 + shared_dir = bzrlib.bzrdir.BzrDir.create(shared_path,
840 + possible_transports=transports)
841 try:
842 shared_repo = shared_dir.open_repository()
843 except bzrlib.errors.NoRepositoryPresent:
@@ -844,7 +850,8 @@ def get_repo(url, alias):
850 else:
851 # check and remove old organization
852 try:
847 - bdir = bzrlib.bzrdir.BzrDir.open(clone_path)
853 + bdir = bzrlib.bzrdir.BzrDir.open(clone_path,
854 + possible_transports=transports)
855 bdir.destroy_repository()
856 except bzrlib.errors.NotBranchError:
857 pass
@@ -897,6 +904,7 @@ def main(args):
904 global files_cache
905 global is_tmp
906 global branches, peers
907 + global transports
908
909 alias = args[1]
910 url = args[2]
@@ -909,6 +917,7 @@ def main(args):
917 marks = None
918 branches = {}
919 peers = {}
920 + transports = []
921
922 if alias[5:] == url:
923 is_tmp = True