Kuang-che Wu | e41e006 | 2017-09-01 19:04:14 +0800 | [diff] [blame] | 1 | # Copyright 2017 The Chromium OS Authors. All rights reserved. |
| 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | """Git utility.""" |
| 5 | |
| 6 | from __future__ import print_function |
| 7 | import logging |
| 8 | import re |
| 9 | import subprocess |
| 10 | |
| 11 | from bisect_kit import cli |
| 12 | from bisect_kit import util |
| 13 | |
| 14 | logger = logging.getLogger(__name__) |
| 15 | |
| 16 | GIT_FULL_COMMIT_ID_LENGTH = 40 |
| 17 | |
| 18 | # Minimal acceptable length of git commit id. |
| 19 | # |
| 20 | # For chromium, hash collision rate over number of digits: |
| 21 | # - 6 digits: 4.85% |
| 22 | # - 7 digits: 0.32% |
| 23 | # - 8 digits: 0.01% |
| 24 | # As foolproof check, 7 digits should be enough. |
| 25 | GIT_MIN_COMMIT_ID_LENGTH = 7 |
| 26 | |
| 27 | |
| 28 | def is_git_rev(s): |
| 29 | """Is a git hash-like version string. |
| 30 | |
| 31 | It accepts shortened hash with at least 7 digits. |
| 32 | """ |
| 33 | if not GIT_MIN_COMMIT_ID_LENGTH <= len(s) <= GIT_FULL_COMMIT_ID_LENGTH: |
| 34 | return False |
| 35 | return bool(re.match(r'^[0-9a-f]+$', s)) |
| 36 | |
| 37 | |
| 38 | def argtype_git_rev(s): |
| 39 | """Validates git hash.""" |
| 40 | if not is_git_rev(s): |
| 41 | msg = 'should be git hash, at least %d digits' % GIT_MIN_COMMIT_ID_LENGTH |
| 42 | raise cli.ArgTypeError(msg, '1a2b3c4d5e') |
| 43 | return s |
| 44 | |
| 45 | |
| 46 | def checkout_version(git_repo, rev): |
| 47 | """git checkout. |
| 48 | |
| 49 | Args: |
| 50 | git_repo: path of git repo. |
| 51 | rev: git commit revision to checkout. |
| 52 | """ |
| 53 | util.check_call('git', 'checkout', '-q', '-f', rev, cwd=git_repo) |
| 54 | |
| 55 | |
| 56 | def is_containing_commit(git_repo, rev): |
| 57 | """Determines given commit exists. |
| 58 | |
| 59 | Args: |
| 60 | git_repo: path of git repo. |
| 61 | rev: git commit revision in query. |
| 62 | """ |
| 63 | try: |
| 64 | return util.check_output( |
| 65 | 'git', 'cat-file', '-t', rev, cwd=git_repo) == 'commit\n' |
| 66 | except subprocess.CalledProcessError: |
| 67 | return False |
| 68 | |
| 69 | |
| 70 | def get_revlist(git_repo, old, new): |
| 71 | """Enumerates git commit between two revisions (inclusive). |
| 72 | |
| 73 | Args: |
| 74 | git_repo: path of git repo. |
| 75 | old: git commit revision. |
| 76 | new: git commit revision. |
| 77 | |
| 78 | Returns: |
| 79 | list of git revisions. The list contains the input revisions, old and new. |
| 80 | """ |
| 81 | assert old |
| 82 | assert new |
| 83 | cmd = ['git', 'rev-list', '--reverse', '%s^..%s' % (old, new)] |
| 84 | revlist = util.check_output(*cmd, cwd=git_repo).splitlines() |
| 85 | return revlist |