blob: d56ad0a82a61bb373f54c15df7cdc6f9452d8782 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001#
2# Copyright (C) 2008 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import os
17import sys
18import subprocess
Shawn O. Pearcefb231612009-04-10 18:53:46 -070019import tempfile
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070020from error import GitError
Shawn O. Pearcead3193a2009-04-18 09:54:51 -070021from trace import REPO_TRACE, IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070022
23GIT = 'git'
24MIN_GIT_VERSION = (1, 5, 4)
25GIT_DIR = 'GIT_DIR'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070026
27LAST_GITDIR = None
28LAST_CWD = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070029
Shawn O. Pearcefb231612009-04-10 18:53:46 -070030_ssh_proxy_path = None
31_ssh_sock_path = None
32
33def _ssh_sock(create=True):
34 global _ssh_sock_path
35 if _ssh_sock_path is None:
36 if not create:
37 return None
Shawn O. Pearced63bbf42009-04-21 08:05:27 -070038 dir = '/tmp'
39 if not os.path.exists(dir):
40 dir = tempfile.gettempdir()
Shawn O. Pearcefb231612009-04-10 18:53:46 -070041 _ssh_sock_path = os.path.join(
Shawn O. Pearced63bbf42009-04-21 08:05:27 -070042 tempfile.mkdtemp('', 'ssh-', dir),
Shawn O. Pearcefb231612009-04-10 18:53:46 -070043 'master-%r@%h:%p')
44 return _ssh_sock_path
45
46def _ssh_proxy():
47 global _ssh_proxy_path
48 if _ssh_proxy_path is None:
49 _ssh_proxy_path = os.path.join(
50 os.path.dirname(__file__),
51 'git_ssh')
52 return _ssh_proxy_path
53
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070054
55class _GitCall(object):
56 def version(self):
57 p = GitCommand(None, ['--version'], capture_stdout=True)
58 if p.Wait() == 0:
59 return p.stdout
60 return None
61
62 def __getattr__(self, name):
63 name = name.replace('_','-')
64 def fun(*cmdv):
65 command = [name]
66 command.extend(cmdv)
67 return GitCommand(None, command).Wait() == 0
68 return fun
69git = _GitCall()
70
Shawn O. Pearce2ec00b92009-06-12 09:32:50 -070071_git_version = None
72
73def git_require(min_version, fail=False):
74 global _git_version
75
76 if _git_version is None:
77 ver_str = git.version()
78 if ver_str.startswith('git version '):
79 _git_version = tuple(
80 map(lambda x: int(x),
81 ver_str[len('git version '):].strip().split('.')[0:3]
82 ))
83 else:
84 print >>sys.stderr, 'fatal: "%s" unsupported' % ver_str
85 sys.exit(1)
86
87 if min_version <= _git_version:
88 return True
89 if fail:
90 need = '.'.join(map(lambda x: str(x), min_version))
91 print >>sys.stderr, 'fatal: git %s or later required' % need
92 sys.exit(1)
93 return False
94
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070095class GitCommand(object):
96 def __init__(self,
97 project,
98 cmdv,
99 bare = False,
100 provide_stdin = False,
101 capture_stdout = False,
102 capture_stderr = False,
103 disable_editor = False,
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700104 ssh_proxy = False,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700105 cwd = None,
106 gitdir = None):
107 env = dict(os.environ)
108
109 for e in [REPO_TRACE,
110 GIT_DIR,
111 'GIT_ALTERNATE_OBJECT_DIRECTORIES',
112 'GIT_OBJECT_DIRECTORY',
113 'GIT_WORK_TREE',
114 'GIT_GRAFT_FILE',
115 'GIT_INDEX_FILE']:
116 if e in env:
117 del env[e]
118
119 if disable_editor:
120 env['GIT_EDITOR'] = ':'
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700121 if ssh_proxy:
122 env['REPO_SSH_SOCK'] = _ssh_sock()
123 env['GIT_SSH'] = _ssh_proxy()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700124
125 if project:
126 if not cwd:
127 cwd = project.worktree
128 if not gitdir:
129 gitdir = project.gitdir
130
131 command = [GIT]
132 if bare:
133 if gitdir:
134 env[GIT_DIR] = gitdir
135 cwd = None
136 command.extend(cmdv)
137
138 if provide_stdin:
139 stdin = subprocess.PIPE
140 else:
141 stdin = None
142
143 if capture_stdout:
144 stdout = subprocess.PIPE
145 else:
146 stdout = None
147
148 if capture_stderr:
149 stderr = subprocess.PIPE
150 else:
151 stderr = None
152
Shawn O. Pearcead3193a2009-04-18 09:54:51 -0700153 if IsTrace():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700154 global LAST_CWD
155 global LAST_GITDIR
156
157 dbg = ''
158
159 if cwd and LAST_CWD != cwd:
160 if LAST_GITDIR or LAST_CWD:
161 dbg += '\n'
162 dbg += ': cd %s\n' % cwd
163 LAST_CWD = cwd
164
165 if GIT_DIR in env and LAST_GITDIR != env[GIT_DIR]:
166 if LAST_GITDIR or LAST_CWD:
167 dbg += '\n'
168 dbg += ': export GIT_DIR=%s\n' % env[GIT_DIR]
169 LAST_GITDIR = env[GIT_DIR]
170
171 dbg += ': '
172 dbg += ' '.join(command)
173 if stdin == subprocess.PIPE:
174 dbg += ' 0<|'
175 if stdout == subprocess.PIPE:
176 dbg += ' 1>|'
177 if stderr == subprocess.PIPE:
178 dbg += ' 2>|'
Shawn O. Pearcead3193a2009-04-18 09:54:51 -0700179 Trace('%s', dbg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700180
181 try:
182 p = subprocess.Popen(command,
183 cwd = cwd,
184 env = env,
185 stdin = stdin,
186 stdout = stdout,
187 stderr = stderr)
188 except Exception, e:
189 raise GitError('%s: %s' % (command[1], e))
190
191 self.process = p
192 self.stdin = p.stdin
193
194 def Wait(self):
195 p = self.process
196
197 if p.stdin:
198 p.stdin.close()
199 self.stdin = None
200
201 if p.stdout:
202 self.stdout = p.stdout.read()
203 p.stdout.close()
204 else:
205 p.stdout = None
206
207 if p.stderr:
208 self.stderr = p.stderr.read()
209 p.stderr.close()
210 else:
211 p.stderr = None
212
213 return self.process.wait()