Index: git_common.py |
diff --git a/git_common.py b/git_common.py |
index 64a385ba1e7f5e1968cdaad66b4921de9b76a0cc..31a62b89043adae8f24da15f88205b0e7083538b 100644 |
--- a/git_common.py |
+++ b/git_common.py |
@@ -91,6 +91,9 @@ GIT_TRANSIENT_ERRORS = ( |
GIT_TRANSIENT_ERRORS_RE = re.compile('|'.join(GIT_TRANSIENT_ERRORS), |
re.IGNORECASE) |
+# First version where the for-each-ref command's format string supported the |
+# upstream:track token. |
+MIN_UPSTREAM_TRACK_GIT_VERSION = (1, 9) |
class BadCommitRefException(Exception): |
def __init__(self, refs): |
@@ -436,8 +439,11 @@ def hash_multi(*reflike): |
return run('rev-parse', *reflike).splitlines() |
-def hash_one(reflike): |
- return run('rev-parse', reflike) |
+def hash_one(reflike, short=False): |
+ args = ['rev-parse', reflike] |
+ if short: |
+ args.insert(1, '--short') |
+ return run(*args) |
def in_rebase(): |
@@ -716,3 +722,46 @@ def upstream(branch): |
branch+'@{upstream}') |
except subprocess2.CalledProcessError: |
return None |
+ |
+def get_git_version(): |
+ """Returns a tuple that contains the numeric components of the current git |
+ version.""" |
+ version_string = run('--version') |
+ version_match = re.search(r'(\d+.)+(\d+)', version_string) |
+ version = version_match.group() if version_match else '' |
+ |
+ return tuple(int(x) for x in version.split('.')) |
+ |
+ |
+def get_all_tracking_info(): |
+ format_string = ( |
+ '--format=%(refname:short):%(objectname:short):%(upstream:short):') |
+ |
+ # This is not covered by the depot_tools CQ which only has git version 1.8. |
+ if get_git_version() >= MIN_UPSTREAM_TRACK_GIT_VERSION: # pragma: no cover |
+ format_string += '%(upstream:track)' |
+ |
+ info_map = {} |
+ data = run('for-each-ref', format_string, 'refs/heads') |
+ TrackingInfo = collections.namedtuple( |
+ 'TrackingInfo', 'hash upstream ahead behind') |
+ for line in data.splitlines(): |
+ (branch, branch_hash, upstream_branch, tracking_status) = line.split(':') |
+ |
+ ahead_match = re.search(r'ahead (\d+)', tracking_status) |
+ ahead = int(ahead_match.group(1)) if ahead_match else None |
+ |
+ behind_match = re.search(r'behind (\d+)', tracking_status) |
+ behind = int(behind_match.group(1)) if behind_match else None |
+ |
+ info_map[branch] = TrackingInfo( |
+ hash=branch_hash, upstream=upstream_branch, ahead=ahead, behind=behind) |
+ |
+ # Set None for upstreams which are not branches (e.g empty upstream, remotes |
+ # and deleted upstream branches). |
+ missing_upstreams = {} |
+ for info in info_map.values(): |
+ if info.upstream not in info_map and info.upstream not in missing_upstreams: |
+ missing_upstreams[info.upstream] = None |
+ |
+ return dict(info_map.items() + missing_upstreams.items()) |