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 MERGE_BASE_TAG_FMT = "gitscripts.merge_base_for_%s" | |
agable
2014/02/28 20:14:16
Ugly that some of these are using a gitscripts con
| |
31 | |
32 | |
30 class DEFAULT(object): | 33 class DEFAULT(object): |
31 def __init__(self, constructor): | 34 def __init__(self, constructor): |
32 self.constructor = constructor | 35 self.constructor = constructor |
33 | 36 |
34 @staticmethod | 37 @staticmethod |
35 def get_from(inst_or_obj): | 38 def get_from(inst_or_obj): |
36 if isinstance(inst_or_obj, DEFAULT): | 39 if isinstance(inst_or_obj, DEFAULT): |
37 return inst_or_obj.constructor() | 40 return inst_or_obj.constructor() |
38 else: | 41 else: |
39 return inst_or_obj | 42 return inst_or_obj |
(...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
217 return run('rev-parse', '--abbrev-ref', ref) | 220 return run('rev-parse', '--abbrev-ref', ref) |
218 | 221 |
219 | 222 |
220 def branches(*args): | 223 def branches(*args): |
221 for line in run('branch', *args).splitlines(): | 224 for line in run('branch', *args).splitlines(): |
222 if line.startswith(NO_BRANCH): | 225 if line.startswith(NO_BRANCH): |
223 continue | 226 continue |
224 yield line.split()[-1] | 227 yield line.split()[-1] |
225 | 228 |
226 | 229 |
230 def clean_refs(): | |
agable
2014/02/28 20:14:16
This is really only cleaning tags, not refs...
| |
231 run('tag', '-d', | |
232 *[t.strip() for t in run('tag', '-l', 'gitscripts.*').split()]) | |
233 | |
234 | |
227 def config_list(option, default=DEFAULT(list)): | 235 def config_list(option, default=DEFAULT(list)): |
228 try: | 236 try: |
229 return run('config', '--get-all', option).split() | 237 return run('config', '--get-all', option).split() |
230 except subprocess2.CalledProcessError: | 238 except subprocess2.CalledProcessError: |
231 return DEFAULT.get_from(default) | 239 return DEFAULT.get_from(default) |
232 | 240 |
233 | 241 |
234 def current_branch(): | 242 def current_branch(): |
235 return abbrev('HEAD') | 243 return abbrev('HEAD') |
236 | 244 |
237 | 245 |
238 def parse_commitrefs(*commitrefs): | 246 def parse_commitrefs(*commitrefs): |
239 """Returns binary encoded commit hashes for one or more commitrefs. | 247 """Returns binary encoded commit hashes for one or more commitrefs. |
240 | 248 |
241 A commitref is anything which can resolve to a commit. Popular examples: | 249 A commitref is anything which can resolve to a commit. Popular examples: |
242 * 'HEAD' | 250 * 'HEAD' |
243 * 'origin/master' | 251 * 'origin/master' |
244 * 'cool_branch~2' | 252 * 'cool_branch~2' |
245 """ | 253 """ |
246 try: | 254 try: |
247 return map(binascii.unhexlify, hash_multi(*commitrefs)) | 255 return map(binascii.unhexlify, hash_multi(*commitrefs)) |
248 except subprocess2.CalledProcessError: | 256 except subprocess2.CalledProcessError: |
249 raise BadCommitRefException(commitrefs) | 257 raise BadCommitRefException(commitrefs) |
250 | 258 |
251 | 259 |
260 def root(): | |
261 return run('config', 'depot_tools.upstream') or 'origin/master' | |
262 | |
263 | |
252 def run(*cmd, **kwargs): | 264 def run(*cmd, **kwargs): |
253 """Runs a git command. Returns stdout as a string. | 265 """Runs a git command. Returns stdout as a string. |
254 | 266 |
255 If logging is DEBUG, we'll print the command before we run it. | 267 If logging is DEBUG, we'll print the command before we run it. |
256 | 268 |
257 kwargs | 269 kwargs |
258 autostrip (bool) - Strip the output. Defaults to True. | 270 autostrip (bool) - Strip the output. Defaults to True. |
259 """ | 271 """ |
260 autostrip = kwargs.pop('autostrip', True) | 272 autostrip = kwargs.pop('autostrip', True) |
261 ret = stream(*cmd, **kwargs).read() | 273 ret = stream(*cmd, **kwargs).read() |
262 if autostrip: | 274 if autostrip: |
263 ret = (ret or '').strip() | 275 ret = (ret or '').strip() |
264 return ret | 276 return ret |
265 | 277 |
266 | 278 |
267 def stream(*cmd, **kwargs): | 279 def stream(*cmd, **kwargs): |
268 """Runs a git command. Returns stdout as a file. | 280 """Runs a git command. Returns stdout as a file. |
269 | 281 |
270 If logging is DEBUG, we'll print the command before we run it. | 282 If logging is DEBUG, we'll print the command before we run it. |
271 """ | 283 """ |
272 cmd = (GIT_EXE,) + cmd | 284 cmd = (GIT_EXE,) + cmd |
273 logging.debug('Running %s', ' '.join(repr(tok) for tok in cmd)) | 285 logging.debug('Running %s', ' '.join(repr(tok) for tok in cmd)) |
274 proc = subprocess2.Popen(cmd, stderr=subprocess2.VOID, | 286 proc = subprocess2.Popen(cmd, stderr=subprocess2.VOID, |
275 stdout=subprocess2.PIPE, **kwargs) | 287 stdout=subprocess2.PIPE, **kwargs) |
276 return proc.stdout | 288 return proc.stdout |
277 | 289 |
278 | 290 |
291 def get_or_create_merge_base_tag(branch, parent): | |
292 tag = MERGE_BASE_TAG_FMT % hash_one(branch) | |
293 tagval = None | |
294 try: | |
295 tagval = hash_one(tag) | |
296 logging.debug('Found tagged merge-base for %s: %s', branch, tagval) | |
297 except subprocess2.CalledProcessError: | |
298 pass | |
299 if not tagval: | |
300 run('tag', '-m', tag, tag, run('merge-base', parent, branch)) | |
301 tagval = hash_one(tag) | |
302 return tagval+'^{}' # this lets rev-parse know this is actually a tag | |
303 | |
304 | |
279 def hash_one(reflike): | 305 def hash_one(reflike): |
280 return run('rev-parse', reflike) | 306 return run('rev-parse', reflike) |
281 | 307 |
282 | 308 |
283 def hash_multi(*reflike): | 309 def hash_multi(*reflike): |
284 return run('rev-parse', *reflike).splitlines() | 310 return run('rev-parse', *reflike).splitlines() |
285 | 311 |
286 | 312 |
287 def intern_f(f, kind='blob'): | 313 def intern_f(f, kind='blob'): |
288 """Interns a file object into the git object store. | 314 """Interns a file object into the git object store. |
289 | 315 |
290 Args: | 316 Args: |
291 f (file-like object) - The file-like object to intern | 317 f (file-like object) - The file-like object to intern |
292 kind (git object type) - One of 'blob', 'commit', 'tree', 'tag'. | 318 kind (git object type) - One of 'blob', 'commit', 'tree', 'tag'. |
293 | 319 |
294 Returns the git hash of the interned object (hex encoded). | 320 Returns the git hash of the interned object (hex encoded). |
295 """ | 321 """ |
296 ret = run('hash-object', '-t', kind, '-w', '--stdin', stdin=f) | 322 ret = run('hash-object', '-t', kind, '-w', '--stdin', stdin=f) |
297 f.close() | 323 f.close() |
298 return ret | 324 return ret |
299 | 325 |
300 | 326 |
327 def manual_merge_base_tag(branch, base): | |
328 tag = MERGE_BASE_TAG_FMT % hash_one(branch) | |
329 run('tag', '-f', '-m', tag, tag, hash_one(base)) | |
330 | |
331 | |
332 def nuke_merge_base_tag(branch): | |
agable
2014/02/28 20:14:16
s/nuke/remove/
| |
333 tag = MERGE_BASE_TAG_FMT % hash_one(branch) | |
334 run('tag', '-d', tag) | |
335 | |
336 | |
301 def tags(*args): | 337 def tags(*args): |
302 for line in run('tag', *args).splitlines(): | 338 for line in run('tag', *args).splitlines(): |
303 if line.startswith(NO_BRANCH): | 339 if line.startswith(NO_BRANCH): |
304 continue | 340 continue |
305 yield line.split()[-1] | 341 yield line.split()[-1] |
306 | 342 |
307 | 343 |
308 def tree(treeref, recurse=False): | 344 def tree(treeref, recurse=False): |
309 """Returns a dict representation of a git tree object. | 345 """Returns a dict representation of a git tree object. |
310 | 346 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
356 See |tree()| for the values of mode, type, and ref. | 392 See |tree()| for the values of mode, type, and ref. |
357 | 393 |
358 Args: | 394 Args: |
359 treedict - { name: (mode, type, ref) } | 395 treedict - { name: (mode, type, ref) } |
360 """ | 396 """ |
361 with tempfile.TemporaryFile() as f: | 397 with tempfile.TemporaryFile() as f: |
362 for name, (mode, typ, ref) in treedict.iteritems(): | 398 for name, (mode, typ, ref) in treedict.iteritems(): |
363 f.write('%s %s %s\t%s\0' % (mode, typ, ref, name)) | 399 f.write('%s %s %s\t%s\0' % (mode, typ, ref, name)) |
364 f.seek(0) | 400 f.seek(0) |
365 return run('mktree', '-z', stdin=f) | 401 return run('mktree', '-z', stdin=f) |
OLD | NEW |