diff options
| -rw-r--r-- | git_command.py | 26 | ||||
| -rw-r--r-- | git_config.py | 81 | ||||
| -rwxr-xr-x | git_ssh | 2 | ||||
| -rwxr-xr-x | main.py | 6 | ||||
| -rw-r--r-- | project.py | 10 |
5 files changed, 122 insertions, 3 deletions
diff --git a/git_command.py b/git_command.py index b6a4a343..954bebad 100644 --- a/git_command.py +++ b/git_command.py | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | import os | 16 | import os |
| 17 | import sys | 17 | import sys |
| 18 | import subprocess | 18 | import subprocess |
| 19 | import tempfile | ||
| 19 | from error import GitError | 20 | from error import GitError |
| 20 | from trace import REPO_TRACE, IsTrace, Trace | 21 | from trace import REPO_TRACE, IsTrace, Trace |
| 21 | 22 | ||
| @@ -26,6 +27,27 @@ GIT_DIR = 'GIT_DIR' | |||
| 26 | LAST_GITDIR = None | 27 | LAST_GITDIR = None |
| 27 | LAST_CWD = None | 28 | LAST_CWD = None |
| 28 | 29 | ||
| 30 | _ssh_proxy_path = None | ||
| 31 | _ssh_sock_path = None | ||
| 32 | |||
| 33 | def _ssh_sock(create=True): | ||
| 34 | global _ssh_sock_path | ||
| 35 | if _ssh_sock_path is None: | ||
| 36 | if not create: | ||
| 37 | return None | ||
| 38 | _ssh_sock_path = os.path.join( | ||
| 39 | tempfile.mkdtemp('', 'ssh-'), | ||
| 40 | 'master-%r@%h:%p') | ||
| 41 | return _ssh_sock_path | ||
| 42 | |||
| 43 | def _ssh_proxy(): | ||
| 44 | global _ssh_proxy_path | ||
| 45 | if _ssh_proxy_path is None: | ||
| 46 | _ssh_proxy_path = os.path.join( | ||
| 47 | os.path.dirname(__file__), | ||
| 48 | 'git_ssh') | ||
| 49 | return _ssh_proxy_path | ||
| 50 | |||
| 29 | 51 | ||
| 30 | class _GitCall(object): | 52 | class _GitCall(object): |
| 31 | def version(self): | 53 | def version(self): |
| @@ -52,6 +74,7 @@ class GitCommand(object): | |||
| 52 | capture_stdout = False, | 74 | capture_stdout = False, |
| 53 | capture_stderr = False, | 75 | capture_stderr = False, |
| 54 | disable_editor = False, | 76 | disable_editor = False, |
| 77 | ssh_proxy = False, | ||
| 55 | cwd = None, | 78 | cwd = None, |
| 56 | gitdir = None): | 79 | gitdir = None): |
| 57 | env = dict(os.environ) | 80 | env = dict(os.environ) |
| @@ -68,6 +91,9 @@ class GitCommand(object): | |||
| 68 | 91 | ||
| 69 | if disable_editor: | 92 | if disable_editor: |
| 70 | env['GIT_EDITOR'] = ':' | 93 | env['GIT_EDITOR'] = ':' |
| 94 | if ssh_proxy: | ||
| 95 | env['REPO_SSH_SOCK'] = _ssh_sock() | ||
| 96 | env['GIT_SSH'] = _ssh_proxy() | ||
| 71 | 97 | ||
| 72 | if project: | 98 | if project: |
| 73 | if not cwd: | 99 | if not cwd: |
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: |
diff --git a/git_ssh b/git_ssh new file mode 100755 index 00000000..63aa63c2 --- /dev/null +++ b/git_ssh | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | exec ssh -o "ControlPath $REPO_SSH_SOCK" "$@" | ||
| @@ -28,6 +28,7 @@ import re | |||
| 28 | import sys | 28 | import sys |
| 29 | 29 | ||
| 30 | from trace import SetTrace | 30 | from trace import SetTrace |
| 31 | from git_config import close_ssh | ||
| 31 | from command import InteractiveCommand | 32 | from command import InteractiveCommand |
| 32 | from command import MirrorSafeCommand | 33 | from command import MirrorSafeCommand |
| 33 | from command import PagedCommand | 34 | from command import PagedCommand |
| @@ -212,7 +213,10 @@ def _Main(argv): | |||
| 212 | 213 | ||
| 213 | repo = _Repo(opt.repodir) | 214 | repo = _Repo(opt.repodir) |
| 214 | try: | 215 | try: |
| 215 | repo._Run(argv) | 216 | try: |
| 217 | repo._Run(argv) | ||
| 218 | finally: | ||
| 219 | close_ssh() | ||
| 216 | except KeyboardInterrupt: | 220 | except KeyboardInterrupt: |
| 217 | sys.exit(1) | 221 | sys.exit(1) |
| 218 | except RepoChangedException, rce: | 222 | except RepoChangedException, rce: |
| @@ -969,11 +969,19 @@ class Project(object): | |||
| 969 | def _RemoteFetch(self, name=None): | 969 | def _RemoteFetch(self, name=None): |
| 970 | if not name: | 970 | if not name: |
| 971 | name = self.remote.name | 971 | name = self.remote.name |
| 972 | |||
| 973 | ssh_proxy = False | ||
| 974 | if self.GetRemote(name).PreConnectFetch(): | ||
| 975 | ssh_proxy = True | ||
| 976 | |||
| 972 | cmd = ['fetch'] | 977 | cmd = ['fetch'] |
| 973 | if not self.worktree: | 978 | if not self.worktree: |
| 974 | cmd.append('--update-head-ok') | 979 | cmd.append('--update-head-ok') |
| 975 | cmd.append(name) | 980 | cmd.append(name) |
| 976 | return GitCommand(self, cmd, bare = True).Wait() == 0 | 981 | return GitCommand(self, |
| 982 | cmd, | ||
| 983 | bare = True, | ||
| 984 | ssh_proxy = ssh_proxy).Wait() == 0 | ||
| 977 | 985 | ||
| 978 | def _Checkout(self, rev, quiet=False): | 986 | def _Checkout(self, rev, quiet=False): |
| 979 | cmd = ['checkout'] | 987 | cmd = ['checkout'] |
