blob: 67f45008296269ea89d57b8776469807ea6df6f3 [file] [log] [blame]
Haibo Huang24950e72018-06-29 14:53:39 -07001# Copyright (C) 2018 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the 'License');
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an 'AS IS' BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14'''Helper functions to communicate with Git.'''
15
16import datetime
Haibo Huang0d3810f2018-08-31 20:44:25 -070017import re
Haibo Huang24950e72018-06-29 14:53:39 -070018import subprocess
19
20
21def _run(cmd, cwd):
22 """Runs a command with stdout and stderr redirected."""
23 return subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
24 check=True, cwd=cwd)
25
Haibo Huang9dcade42018-08-03 11:52:25 -070026
Haibo Huang24950e72018-06-29 14:53:39 -070027def fetch(proj_path, remote_names):
28 """Runs git fetch.
29
30 Args:
31 proj_path: Path to Git repository.
32 remote_names: Array of string to specify remote names.
33 """
34 _run(['git', 'fetch', '--multiple'] + remote_names, cwd=proj_path)
35
Haibo Huang9dcade42018-08-03 11:52:25 -070036
Haibo Huang24950e72018-06-29 14:53:39 -070037def add_remote(proj_path, name, url):
38 """Adds a git remote.
39
40 Args:
41 proj_path: Path to Git repository.
42 name: Name of the new remote.
43 url: Url of the new remote.
44 """
45 _run(['git', 'remote', 'add', name, url], cwd=proj_path)
46
Haibo Huang9dcade42018-08-03 11:52:25 -070047
Haibo Huang24950e72018-06-29 14:53:39 -070048def list_remotes(proj_path):
49 """Lists all Git remotes.
50
51 Args:
52 proj_path: Path to Git repository.
53
54 Returns:
55 A dict from remote name to remote url.
56 """
57 out = _run(['git', 'remote', '-v'], proj_path)
58 lines = out.stdout.decode('utf-8').splitlines()
59 return dict([line.split()[0:2] for line in lines])
60
Haibo Huang9dcade42018-08-03 11:52:25 -070061
Haibo Huang24950e72018-06-29 14:53:39 -070062def get_commits_ahead(proj_path, branch, base_branch):
63 """Lists commits in `branch` but not `base_branch`."""
Haibo Huang80a05422018-08-27 13:49:42 -070064 out = _run(['git', 'rev-list', '--left-only', '--ancestry-path',
Haibo Huang24950e72018-06-29 14:53:39 -070065 '{}...{}'.format(branch, base_branch)],
66 proj_path)
67 return out.stdout.decode('utf-8').splitlines()
68
Haibo Huang9dcade42018-08-03 11:52:25 -070069
Haibo Huang24950e72018-06-29 14:53:39 -070070def get_commit_time(proj_path, commit):
71 """Gets commit time of one commit."""
72 out = _run(['git', 'show', '-s', '--format=%ct', commit], cwd=proj_path)
73 return datetime.datetime.fromtimestamp(int(out.stdout))
74
Haibo Huang9dcade42018-08-03 11:52:25 -070075
Haibo Huang24950e72018-06-29 14:53:39 -070076def list_remote_branches(proj_path, remote_name):
77 """Lists all branches for a remote."""
78 out = _run(['git', 'branch', '-r'], cwd=proj_path)
79 lines = out.stdout.decode('utf-8').splitlines()
80 stripped = [line.strip() for line in lines]
81 remote_path = remote_name + '/'
82 remote_path_len = len(remote_path)
83 return [line[remote_path_len:] for line in stripped
84 if line.startswith(remote_path)]
Haibo Huang0d3810f2018-08-31 20:44:25 -070085
86
87def _parse_remote_tag(line):
88 tag_prefix = 'refs/tags/'
89 tag_suffix = '^{}'
90 try:
91 line = line[line.index(tag_prefix):]
92 except ValueError:
93 return None
94 line = line[len(tag_prefix):]
95 if line.endswith(tag_suffix):
96 line = line[:-len(tag_suffix)]
97 return line
98
99
100def list_remote_tags(proj_path, remote_name):
101 """Lists all tags for a remote."""
102 out = _run(['git', "ls-remote", "--tags", remote_name],
103 cwd=proj_path)
104 lines = out.stdout.decode('utf-8').splitlines()
105 tags = [_parse_remote_tag(line) for line in lines]
106 return list(set(tags))
107
108
109COMMIT_PATTERN = r'^[a-f0-9]{40}$'
110COMMIT_RE = re.compile(COMMIT_PATTERN)
111
112
113def is_commit(commit):
Haibo Huangcd2c6122018-09-04 14:24:20 -0700114 """Whether a string looks like a SHA1 hash."""
Haibo Huang0d3810f2018-08-31 20:44:25 -0700115 return bool(COMMIT_RE.match(commit))
Haibo Huang0cabc2e2019-01-18 13:29:48 -0800116
117
118def merge(proj_path, branch):
119 """Merges a branch."""
120 _run(['git', 'merge', branch], cwd=proj_path)