OLD | NEW |
---|---|
1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 # Monkeypatch IMapIterator so that Ctrl-C can kill everything properly. | 5 # Monkeypatch IMapIterator so that Ctrl-C can kill everything properly. |
6 # Derived from https://gist.github.com/aljungberg/626518 | 6 # Derived from https://gist.github.com/aljungberg/626518 |
7 import multiprocessing.pool | 7 import multiprocessing.pool |
8 from multiprocessing.pool import IMapIterator | 8 from multiprocessing.pool import IMapIterator |
9 def wrapper(func): | 9 def wrapper(func): |
10 def wrap(self, timeout=None): | 10 def wrap(self, timeout=None): |
11 return func(self, timeout=timeout or 1e100) | 11 return func(self, timeout=timeout or 1e100) |
12 return wrap | 12 return wrap |
13 IMapIterator.next = wrapper(IMapIterator.next) | 13 IMapIterator.next = wrapper(IMapIterator.next) |
14 IMapIterator.__next__ = IMapIterator.next | 14 IMapIterator.__next__ = IMapIterator.next |
15 # TODO(iannucci): Monkeypatch all other 'wait' methods too. | 15 # TODO(iannucci): Monkeypatch all other 'wait' methods too. |
16 | 16 |
17 | 17 |
18 import binascii | 18 import binascii |
19 import contextlib | 19 import contextlib |
20 import functools | 20 import functools |
21 import logging | 21 import logging |
22 import signal | 22 import signal |
23 import sys | 23 import sys |
24 import tempfile | 24 import tempfile |
25 import threading | 25 import threading |
26 | 26 |
27 import subprocess2 | 27 import subprocess2 |
28 | 28 |
29 | 29 |
30 class DEFAULT(object): | |
agable
2014/02/28 19:54:58
Why? This is so completely unnecessary. Maybe kind
iannucci
2014/03/06 00:18:39
*sigh* going back to useless-mode. lcd for the win
| |
31 def __init__(self, constructor): | |
32 self.constructor = constructor | |
33 | |
34 @staticmethod | |
35 def get_from(inst_or_obj): | |
36 if isinstance(inst_or_obj, DEFAULT): | |
37 return inst_or_obj.constructor() | |
38 else: | |
39 return inst_or_obj | |
40 | |
41 | |
30 GIT_EXE = 'git.bat' if sys.platform.startswith('win') else 'git' | 42 GIT_EXE = 'git.bat' if sys.platform.startswith('win') else 'git' |
31 | 43 |
44 NO_BRANCH = ('* (no branch)', '* (detached from ') | |
45 | |
32 | 46 |
33 class BadCommitRefException(Exception): | 47 class BadCommitRefException(Exception): |
34 def __init__(self, refs): | 48 def __init__(self, refs): |
35 msg = ('one of %s does not seem to be a valid commitref.' % | 49 msg = ('one of %s does not seem to be a valid commitref.' % |
36 str(refs)) | 50 str(refs)) |
37 super(BadCommitRefException, self).__init__(msg) | 51 super(BadCommitRefException, self).__init__(msg) |
38 | 52 |
39 | 53 |
40 def memoize_one(**kwargs): | 54 def memoize_one(**kwargs): |
41 """Memoizes a single-argument pure function. | 55 """Memoizes a single-argument pure function. |
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
192 return self.inc | 206 return self.inc |
193 | 207 |
194 def __exit__(self, _exc_type, _exc_value, _traceback): | 208 def __exit__(self, _exc_type, _exc_value, _traceback): |
195 self._dead = True | 209 self._dead = True |
196 with self._dead_cond: | 210 with self._dead_cond: |
197 self._dead_cond.notifyAll() | 211 self._dead_cond.notifyAll() |
198 self._thread.join() | 212 self._thread.join() |
199 del self._thread | 213 del self._thread |
200 | 214 |
201 | 215 |
216 def abbrev(ref): | |
217 return run('rev-parse', '--abbrev-ref', ref) | |
218 | |
219 | |
220 def branches(*args): | |
221 for line in run('branch', *args).splitlines(): | |
222 if line.startswith(NO_BRANCH): | |
223 continue | |
224 yield line.split()[-1] | |
225 | |
226 | |
227 def config_list(option, default=DEFAULT(list)): | |
agable
2014/02/28 19:54:58
default=None
if default is None: default=[]
Let's
iannucci
2014/03/06 00:18:39
sometimes I hate python. The =None idiom is so uni
agable
2014/03/06 18:09:07
I know, and I totally agree. But I don't think thi
| |
228 try: | |
229 return run('config', '--get-all', option).split() | |
230 except subprocess2.CalledProcessError: | |
231 return DEFAULT.get_from(default) | |
232 | |
233 | |
234 def current_branch(): | |
235 return abbrev('HEAD') | |
236 | |
237 | |
202 def parse_commitrefs(*commitrefs): | 238 def parse_commitrefs(*commitrefs): |
203 """Returns binary encoded commit hashes for one or more commitrefs. | 239 """Returns binary encoded commit hashes for one or more commitrefs. |
204 | 240 |
205 A commitref is anything which can resolve to a commit. Popular examples: | 241 A commitref is anything which can resolve to a commit. Popular examples: |
206 * 'HEAD' | 242 * 'HEAD' |
207 * 'origin/master' | 243 * 'origin/master' |
208 * 'cool_branch~2' | 244 * 'cool_branch~2' |
209 """ | 245 """ |
210 try: | 246 try: |
211 return map(binascii.unhexlify, hashes(*commitrefs)) | 247 return map(binascii.unhexlify, hash_multi(*commitrefs)) |
212 except subprocess2.CalledProcessError: | 248 except subprocess2.CalledProcessError: |
213 raise BadCommitRefException(commitrefs) | 249 raise BadCommitRefException(commitrefs) |
214 | 250 |
215 | 251 |
216 def run(*cmd, **kwargs): | 252 def run(*cmd, **kwargs): |
217 """Runs a git command. Returns stdout as a string. | 253 """Runs a git command. Returns stdout as a string. |
218 | 254 |
219 If logging is DEBUG, we'll print the command before we run it. | 255 If logging is DEBUG, we'll print the command before we run it. |
220 | 256 |
221 kwargs | 257 kwargs |
222 autostrip (bool) - Strip the output. Defaults to True. | 258 autostrip (bool) - Strip the output. Defaults to True. |
223 Output string is always strip()'d. | 259 Output string is always strip()'d. |
224 """ | 260 """ |
225 autostrip = kwargs.pop('autostrip', True) | 261 autostrip = kwargs.pop('autostrip', True) |
226 cmd = (GIT_EXE,) + cmd | 262 cmd = (GIT_EXE,) + cmd |
227 logging.debug('Running %s', ' '.join(repr(tok) for tok in cmd)) | 263 logging.debug('Running %s', ' '.join(repr(tok) for tok in cmd)) |
228 ret = subprocess2.check_output(cmd, stderr=subprocess2.PIPE, **kwargs) | 264 ret = subprocess2.check_output(cmd, stderr=subprocess2.PIPE, **kwargs) |
229 if autostrip: | 265 if autostrip: |
230 ret = (ret or '').strip() | 266 ret = (ret or '').strip() |
231 return ret | 267 return ret |
232 | 268 |
233 | 269 |
234 def hashes(*reflike): | 270 def hash_one(reflike): |
271 return run('rev-parse', reflike) | |
272 | |
273 | |
274 def hash_multi(*reflike): | |
agable
2014/02/28 19:54:58
This should be "*reflikes" (cf. "*args").
iannucci
2014/03/06 00:18:39
Done.
| |
235 return run('rev-parse', *reflike).splitlines() | 275 return run('rev-parse', *reflike).splitlines() |
236 | 276 |
237 | 277 |
238 def intern_f(f, kind='blob'): | 278 def intern_f(f, kind='blob'): |
239 """Interns a file object into the git object store. | 279 """Interns a file object into the git object store. |
240 | 280 |
241 Args: | 281 Args: |
242 f (file-like object) - The file-like object to intern | 282 f (file-like object) - The file-like object to intern |
243 kind (git object type) - One of 'blob', 'commit', 'tree', 'tag'. | 283 kind (git object type) - One of 'blob', 'commit', 'tree', 'tag'. |
244 | 284 |
245 Returns the git hash of the interned object (hex encoded). | 285 Returns the git hash of the interned object (hex encoded). |
246 """ | 286 """ |
247 ret = run('hash-object', '-t', kind, '-w', '--stdin', stdin=f) | 287 ret = run('hash-object', '-t', kind, '-w', '--stdin', stdin=f) |
248 f.close() | 288 f.close() |
249 return ret | 289 return ret |
250 | 290 |
251 | 291 |
292 def tags(*args): | |
293 for line in run('tag', *args).splitlines(): | |
294 if line.startswith(NO_BRANCH): | |
295 continue | |
296 yield line.split()[-1] | |
297 | |
298 | |
252 def tree(treeref, recurse=False): | 299 def tree(treeref, recurse=False): |
253 """Returns a dict representation of a git tree object. | 300 """Returns a dict representation of a git tree object. |
254 | 301 |
255 Args: | 302 Args: |
256 treeref (str) - a git ref which resolves to a tree (commits count as trees). | 303 treeref (str) - a git ref which resolves to a tree (commits count as trees). |
257 recurse (bool) - include all of the tree's decendants too. File names will | 304 recurse (bool) - include all of the tree's decendants too. File names will |
258 take the form of 'some/path/to/file'. | 305 take the form of 'some/path/to/file'. |
259 | 306 |
260 Return format: | 307 Return format: |
261 { 'file_name': (mode, type, ref) } | 308 { 'file_name': (mode, type, ref) } |
(...skipping 17 matching lines...) Expand all Loading... | |
279 opts.append(treeref) | 326 opts.append(treeref) |
280 try: | 327 try: |
281 for line in run(*opts).splitlines(): | 328 for line in run(*opts).splitlines(): |
282 mode, typ, ref, name = line.split(None, 3) | 329 mode, typ, ref, name = line.split(None, 3) |
283 ret[name] = (mode, typ, ref) | 330 ret[name] = (mode, typ, ref) |
284 except subprocess2.CalledProcessError: | 331 except subprocess2.CalledProcessError: |
285 return None | 332 return None |
286 return ret | 333 return ret |
287 | 334 |
288 | 335 |
336 def upstream(branch): | |
337 try: | |
338 return run('rev-parse', '--abbrev-ref', '--symbolic-full-name', | |
339 branch+'@{upstream}') | |
340 except subprocess2.CalledProcessError: | |
341 return None | |
342 | |
343 | |
289 def mktree(treedict): | 344 def mktree(treedict): |
290 """Makes a git tree object and returns its hash. | 345 """Makes a git tree object and returns its hash. |
291 | 346 |
292 See |tree()| for the values of mode, type, and ref. | 347 See |tree()| for the values of mode, type, and ref. |
293 | 348 |
294 Args: | 349 Args: |
295 treedict - { name: (mode, type, ref) } | 350 treedict - { name: (mode, type, ref) } |
296 """ | 351 """ |
297 with tempfile.TemporaryFile() as f: | 352 with tempfile.TemporaryFile() as f: |
298 for name, (mode, typ, ref) in treedict.iteritems(): | 353 for name, (mode, typ, ref) in treedict.iteritems(): |
299 f.write('%s %s %s\t%s\0' % (mode, typ, ref, name)) | 354 f.write('%s %s %s\t%s\0' % (mode, typ, ref, name)) |
300 f.seek(0) | 355 f.seek(0) |
301 return run('mktree', '-z', stdin=f) | 356 return run('mktree', '-z', stdin=f) |
OLD | NEW |