| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2013 The Chromium Authors. All rights reserved. | 2 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Usage: %prog [options] [<commitref>]* | 6 """Usage: %prog [options] [<commitref>]* |
| 7 | 7 |
| 8 If no <commitref>'s are supplied, it defaults to HEAD. | 8 If no <commitref>'s are supplied, it defaults to HEAD. |
| 9 | 9 |
| 10 Calculates the generation number for one or more commits in a git repo. | 10 Calculates the generation number for one or more commits in a git repo. |
| (...skipping 19 matching lines...) Expand all Loading... |
| 30 import sys | 30 import sys |
| 31 import tempfile | 31 import tempfile |
| 32 | 32 |
| 33 import git_common as git | 33 import git_common as git |
| 34 import subprocess2 | 34 import subprocess2 |
| 35 | 35 |
| 36 CHUNK_FMT = '!20sL' | 36 CHUNK_FMT = '!20sL' |
| 37 CHUNK_SIZE = struct.calcsize(CHUNK_FMT) | 37 CHUNK_SIZE = struct.calcsize(CHUNK_FMT) |
| 38 DIRTY_TREES = collections.defaultdict(int) | 38 DIRTY_TREES = collections.defaultdict(int) |
| 39 REF = 'refs/number/commits' | 39 REF = 'refs/number/commits' |
| 40 AUTHOR_NAME = 'git-number' |
| 41 AUTHOR_EMAIL = 'chrome-infrastructure-team@google.com' |
| 40 | 42 |
| 41 # Number of bytes to use for the prefix on our internal number structure. | 43 # Number of bytes to use for the prefix on our internal number structure. |
| 42 # 0 is slow to deserialize. 2 creates way too much bookeeping overhead (would | 44 # 0 is slow to deserialize. 2 creates way too much bookeeping overhead (would |
| 43 # need to reimplement cache data structures to be a bit more sophisticated than | 45 # need to reimplement cache data structures to be a bit more sophisticated than |
| 44 # dicts. 1 seems to be just right. | 46 # dicts. 1 seems to be just right. |
| 45 PREFIX_LEN = 1 | 47 PREFIX_LEN = 1 |
| 46 | 48 |
| 47 # Set this to 'threads' to gather coverage data while testing. | 49 # Set this to 'threads' to gather coverage data while testing. |
| 48 POOL_KIND = 'procs' | 50 POOL_KIND = 'procs' |
| 49 | 51 |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 146 with git.ScopedPool(kind=POOL_KIND) as leaf_pool: | 148 with git.ScopedPool(kind=POOL_KIND) as leaf_pool: |
| 147 for item in leaf_pool.imap(leaf_map_fn, prefixes_trees): | 149 for item in leaf_pool.imap(leaf_map_fn, prefixes_trees): |
| 148 updater.stdin.write(item) | 150 updater.stdin.write(item) |
| 149 inc() | 151 inc() |
| 150 | 152 |
| 151 updater.stdin.close() | 153 updater.stdin.close() |
| 152 updater.wait() | 154 updater.wait() |
| 153 assert updater.returncode == 0 | 155 assert updater.returncode == 0 |
| 154 | 156 |
| 155 tree_id = git.run('write-tree', env=env) | 157 tree_id = git.run('write-tree', env=env) |
| 156 commit_cmd = ['commit-tree', '-m', msg, '-p'] + git.hashes(REF) | 158 commit_cmd = [ |
| 159 # Git user.name and/or user.email may not be configured, so specifying |
| 160 # them explicitly. They are not used, but requried by Git. |
| 161 '-c', 'user.name=%s' % AUTHOR_NAME, |
| 162 '-c', 'user.email=%s' % AUTHOR_EMAIL, |
| 163 'commit-tree', |
| 164 '-m', msg, |
| 165 '-p'] + git.hash_multi(REF) |
| 157 for t in targets: | 166 for t in targets: |
| 158 commit_cmd.extend(['-p', binascii.hexlify(t)]) | 167 commit_cmd.extend(['-p', binascii.hexlify(t)]) |
| 159 commit_cmd.append(tree_id) | 168 commit_cmd.append(tree_id) |
| 160 commit_hash = git.run(*commit_cmd) | 169 commit_hash = git.run(*commit_cmd) |
| 161 git.run('update-ref', REF, commit_hash) | 170 git.run('update-ref', REF, commit_hash) |
| 162 DIRTY_TREES.clear() | 171 DIRTY_TREES.clear() |
| 163 | 172 |
| 164 | 173 |
| 165 def preload_tree(prefix): | 174 def preload_tree(prefix): |
| 166 """Returns the prefix and parsed tree object for the specified prefix.""" | 175 """Returns the prefix and parsed tree object for the specified prefix.""" |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 236 help='Do not actually cache anything we calculate.') | 245 help='Do not actually cache anything we calculate.') |
| 237 parser.add_option('--reset', action='store_true', | 246 parser.add_option('--reset', action='store_true', |
| 238 help='Reset the generation number cache and quit.') | 247 help='Reset the generation number cache and quit.') |
| 239 parser.add_option('-v', '--verbose', action='count', default=0, | 248 parser.add_option('-v', '--verbose', action='count', default=0, |
| 240 help='Be verbose. Use more times for more verbosity.') | 249 help='Be verbose. Use more times for more verbosity.') |
| 241 opts, args = parser.parse_args() | 250 opts, args = parser.parse_args() |
| 242 | 251 |
| 243 levels = [logging.ERROR, logging.INFO, logging.DEBUG] | 252 levels = [logging.ERROR, logging.INFO, logging.DEBUG] |
| 244 logging.basicConfig(level=levels[min(opts.verbose, len(levels) - 1)]) | 253 logging.basicConfig(level=levels[min(opts.verbose, len(levels) - 1)]) |
| 245 | 254 |
| 255 # 'git number' should only be used on bots. |
| 256 if os.getenv('CHROME_HEADLESS') != '1': |
| 257 logging.error("'git-number' is an infrastructure tool that is only " |
| 258 "intended to be used internally by bots. Developers should " |
| 259 "use the 'Cr-Commit-Position' value in the commit's message.") |
| 260 return 1 |
| 261 |
| 262 if opts.reset: |
| 263 clear_caches(on_disk=True) |
| 264 return |
| 265 |
| 246 try: | 266 try: |
| 247 if opts.reset: | 267 targets = git.parse_commitrefs(*(args or ['HEAD'])) |
| 248 clear_caches(on_disk=True) | 268 except git.BadCommitRefException as e: |
| 249 return | 269 parser.error(e) |
| 250 | 270 |
| 251 try: | 271 load_generation_numbers(targets) |
| 252 targets = git.parse_commitrefs(*(args or ['HEAD'])) | 272 if not opts.no_cache: |
| 253 except git.BadCommitRefException as e: | 273 finalize(targets) |
| 254 parser.error(e) | |
| 255 | 274 |
| 256 load_generation_numbers(targets) | 275 print '\n'.join(map(str, map(get_num, targets))) |
| 257 if not opts.no_cache: | 276 return 0 |
| 258 finalize(targets) | |
| 259 | |
| 260 print '\n'.join(map(str, map(get_num, targets))) | |
| 261 return 0 | |
| 262 except KeyboardInterrupt: | |
| 263 return 1 | |
| 264 | 277 |
| 265 | 278 |
| 266 if __name__ == '__main__': # pragma: no cover | 279 if __name__ == '__main__': # pragma: no cover |
| 267 sys.exit(main()) | 280 try: |
| 281 sys.exit(main()) |
| 282 except KeyboardInterrupt: |
| 283 sys.stderr.write('interrupted\n') |
| 284 sys.exit(1) |
| OLD | NEW |