diff options
author | Shawn O. Pearce <sop@google.com> | 2009-04-10 18:53:46 -0700 |
---|---|---|
committer | Shawn O. Pearce <sop@google.com> | 2009-04-18 16:50:47 -0700 |
commit | fb2316146f6e3036e0cc3e08653920964a428a15 (patch) | |
tree | f19c5c65a035f547ada03496f1576524e4602665 /git_config.py | |
parent | 8bd5e60b16080008771afcaa7de7084487b84780 (diff) | |
download | git-repo-fb2316146f6e3036e0cc3e08653920964a428a15.tar.gz |
Automatically use SSH control master support during sync
By creating a background ssh "control master" process which lives
for the duration of our sync cycle we can easily cut the time for
a no-op sync of 132 projects from 60s to 18s.
Bug: REPO-11
Signed-off-by: Shawn O. Pearce <sop@google.com>
Diffstat (limited to 'git_config.py')
-rw-r--r-- | git_config.py | 81 |
1 files changed, 80 insertions, 1 deletions
diff --git a/git_config.py b/git_config.py index 7e642a4c..163b0809 100644 --- a/git_config.py +++ b/git_config.py | |||
@@ -16,11 +16,14 @@ | |||
16 | import cPickle | 16 | import cPickle |
17 | import os | 17 | import os |
18 | import re | 18 | import re |
19 | import subprocess | ||
19 | import sys | 20 | import sys |
21 | import time | ||
22 | from signal import SIGTERM | ||
20 | from urllib2 import urlopen, HTTPError | 23 | from urllib2 import urlopen, HTTPError |
21 | from error import GitError, UploadError | 24 | from error import GitError, UploadError |
22 | from trace import Trace | 25 | from trace import Trace |
23 | from git_command import GitCommand | 26 | from git_command import GitCommand, _ssh_sock |
24 | 27 | ||
25 | R_HEADS = 'refs/heads/' | 28 | R_HEADS = 'refs/heads/' |
26 | R_TAGS = 'refs/tags/' | 29 | R_TAGS = 'refs/tags/' |
@@ -331,6 +334,79 @@ class RefSpec(object): | |||
331 | return s | 334 | return s |
332 | 335 | ||
333 | 336 | ||
337 | _ssh_cache = {} | ||
338 | _ssh_master = True | ||
339 | |||
340 | def _open_ssh(host, port=None): | ||
341 | global _ssh_master | ||
342 | |||
343 | if port is None: | ||
344 | port = 22 | ||
345 | |||
346 | key = '%s:%s' % (host, port) | ||
347 | if key in _ssh_cache: | ||
348 | return True | ||
349 | |||
350 | if not _ssh_master \ | ||
351 | or 'GIT_SSH' in os.environ \ | ||
352 | or sys.platform == 'win32': | ||
353 | # failed earlier, or cygwin ssh can't do this | ||
354 | # | ||
355 | return False | ||
356 | |||
357 | command = ['ssh', | ||
358 | '-o','ControlPath %s' % _ssh_sock(), | ||
359 | '-p',str(port), | ||
360 | '-M', | ||
361 | '-N', | ||
362 | host] | ||
363 | try: | ||
364 | Trace(': %s', ' '.join(command)) | ||
365 | p = subprocess.Popen(command) | ||
366 | except Exception, e: | ||
367 | _ssh_master = False | ||
368 | print >>sys.stderr, \ | ||
369 | '\nwarn: cannot enable ssh control master for %s:%s\n%s' \ | ||
370 | % (host,port, str(e)) | ||
371 | return False | ||
372 | |||
373 | _ssh_cache[key] = p | ||
374 | time.sleep(1) | ||
375 | return True | ||
376 | |||
377 | def close_ssh(): | ||
378 | for key,p in _ssh_cache.iteritems(): | ||
379 | os.kill(p.pid, SIGTERM) | ||
380 | p.wait() | ||
381 | _ssh_cache.clear() | ||
382 | |||
383 | d = _ssh_sock(create=False) | ||
384 | if d: | ||
385 | try: | ||
386 | os.rmdir(os.path.dirname(d)) | ||
387 | except OSError: | ||
388 | pass | ||
389 | |||
390 | URI_SCP = re.compile(r'^([^@:]*@?[^:/]{1,}):') | ||
391 | URI_ALL = re.compile(r'^([a-z][a-z+]*)://([^@/]*@?[^/])/') | ||
392 | |||
393 | def _preconnect(url): | ||
394 | m = URI_ALL.match(url) | ||
395 | if m: | ||
396 | scheme = m.group(1) | ||
397 | host = m.group(2) | ||
398 | if ':' in host: | ||
399 | host, port = host.split(':') | ||
400 | if scheme in ('ssh', 'git+ssh', 'ssh+git'): | ||
401 | return _open_ssh(host, port) | ||
402 | return False | ||
403 | |||
404 | m = URI_SCP.match(url) | ||
405 | if m: | ||
406 | host = m.group(1) | ||
407 | return _open_ssh(host) | ||
408 | |||
409 | |||
334 | class Remote(object): | 410 | class Remote(object): |
335 | """Configuration options related to a remote. | 411 | """Configuration options related to a remote. |
336 | """ | 412 | """ |
@@ -344,6 +420,9 @@ class Remote(object): | |||
344 | self._Get('fetch', all=True)) | 420 | self._Get('fetch', all=True)) |
345 | self._review_protocol = None | 421 | self._review_protocol = None |
346 | 422 | ||
423 | def PreConnectFetch(self): | ||
424 | return _preconnect(self.url) | ||
425 | |||
347 | @property | 426 | @property |
348 | def ReviewProtocol(self): | 427 | def ReviewProtocol(self): |
349 | if self._review_protocol is None: | 428 | if self._review_protocol is None: |