-
Notifications
You must be signed in to change notification settings - Fork 346
Expand file tree
/
Copy pathgit.py
More file actions
175 lines (132 loc) · 6.57 KB
/
Copy pathgit.py
File metadata and controls
175 lines (132 loc) · 6.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# Licensed to Elasticsearch B.V. under one or more contributor
# license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright
# ownership. Elasticsearch B.V. licenses this file to you under
# the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import logging
import os
from esrally import exceptions
from esrally.utils import io, process
GIT_TIMEOUT = 600
def probed(f):
def probe(src, *args, **kwargs):
# Probe for -C
if not process.exit_status_as_bool(
lambda: process.run_subprocess_with_logging(
f"git -C {io.escape_path(src)} --version", level=logging.DEBUG, timeout=GIT_TIMEOUT
),
quiet=True,
):
version = process.run_subprocess_with_output("git --version")
if version:
version = str(version).strip()
else:
version = "Unknown"
raise exceptions.SystemSetupError("Your git version is [%s] but Rally requires at least git 1.9. Please update git." % version)
return f(src, *args, **kwargs)
return probe
def is_working_copy(src):
"""
Checks whether the given directory is a git working copy.
:param src: A directory. May or may not exist.
:return: True iff the given directory is a git working copy.
"""
return os.path.exists(src) and os.path.exists(os.path.join(src, ".git"))
@probed
def is_branch(src_dir, identifier):
show_ref_cmd = f"git -C {src_dir} show-ref {identifier}"
completed_process = process.run_subprocess_with_logging_and_output(show_ref_cmd)
# if we get an non-zero exit code, we know that the identifier is not a branch (local or remote)
if not process.exit_status_as_bool(lambda: completed_process.returncode):
return False
# it's possible the identifier could be a tag, so we explicitly check that here
ref = completed_process.stdout.split("\n")
if "refs/tags" in ref[0]:
return False
return True
def clone(src, *, remote):
io.ensure_dir(src)
# Don't swallow subprocess output, user might need to enter credentials...
if process.run_subprocess_with_logging("git clone %s %s" % (remote, io.escape_path(src)), timeout=GIT_TIMEOUT):
raise exceptions.SupplyError("Could not clone from [%s] to [%s]" % (remote, src))
@probed
def fetch(src, *, remote):
if process.run_subprocess_with_logging(f"git -C {io.escape_path(src)} fetch --prune --tags {remote}", timeout=GIT_TIMEOUT):
raise exceptions.SupplyError("Could not fetch source tree from [%s]" % remote)
@probed
def checkout(src_dir, *, branch):
if process.run_subprocess_with_logging(f"git -C {io.escape_path(src_dir)} checkout {branch}", timeout=GIT_TIMEOUT):
raise exceptions.SupplyError("Could not checkout [%s]. Do you have uncommitted changes?" % branch)
@probed
def checkout_branch(src_dir, remote, branch):
if process.run_subprocess_with_logging(f"git -C {io.escape_path(src_dir)} checkout {remote}/{branch}", timeout=GIT_TIMEOUT):
raise exceptions.SupplyError("Could not checkout [%s]. Do you have uncommitted changes?" % branch)
@probed
def rebase(src_dir, *, remote, branch):
checkout(src_dir, branch=branch)
if process.run_subprocess_with_logging(f"git -C {io.escape_path(src_dir)} rebase {remote}/{branch}", timeout=GIT_TIMEOUT):
raise exceptions.SupplyError("Could not rebase on branch [%s]" % branch)
@probed
def pull(src_dir, *, remote, branch):
fetch(src_dir, remote=remote)
rebase(src_dir, remote=remote, branch=branch)
@probed
def pull_ts(src_dir, ts, *, remote, branch, default_branch):
fetch(src_dir, remote=remote)
clean_src = io.escape_path(src_dir)
# non-default ES branches might receive merges from default ES branch which we want to filter out
if branch != default_branch:
rev_list_command = f'git -C {clean_src} rev-list -n 1 --before="{ts}" --date=iso8601 {remote}/{default_branch}..{remote}/{branch}'
else:
rev_list_command = f'git -C {clean_src} rev-list -n 1 --before="{ts}" --date=iso8601 {remote}/{branch}'
revision = process.run_subprocess_with_output(rev_list_command)[0].strip()
if process.run_subprocess_with_logging(f"git -C {clean_src} checkout {revision}", timeout=GIT_TIMEOUT):
raise exceptions.SupplyError("Could not checkout source tree for timestamped revision [%s]" % ts)
@probed
def checkout_revision(src_dir, *, revision):
if process.run_subprocess_with_logging(f"git -C {io.escape_path(src_dir)} checkout {revision}", timeout=GIT_TIMEOUT):
raise exceptions.SupplyError("Could not checkout source tree for revision [%s]" % revision)
@probed
def head_revision(src_dir):
return process.run_subprocess_with_output(f"git -C {io.escape_path(src_dir)} rev-parse HEAD")[0].strip()
@probed
def current_branch(src_dir):
return process.run_subprocess_with_output(f"git -C {io.escape_path(src_dir)} rev-parse --abbrev-ref HEAD")[0].strip()
@probed
def branches(src_dir, remote=True):
clean_src = io.escape_path(src_dir)
if remote:
# alternatively: git for-each-ref refs/remotes/ --format='%(refname:short)'
return _cleanup_remote_branch_names(
process.run_subprocess_with_output(f"git -C {clean_src} for-each-ref refs/remotes/ --format='%(refname:short)'")
)
else:
return _cleanup_local_branch_names(
process.run_subprocess_with_output(f"git -C {clean_src} for-each-ref refs/heads/ --format='%(refname:short)'")
)
@probed
def tags(src_dir):
return _cleanup_tag_names(process.run_subprocess_with_output(f"git -C {io.escape_path(src_dir)} tag"))
def _cleanup_remote_branch_names(refs):
branches = []
for ref in refs:
# git >= 2.40.0 reports an `origin` ref without a slash while previous versions
# reported a `origin/HEAD` ref.
if "/" in ref and not ref.endswith("/HEAD"):
branches.append(ref[ref.index("/") + 1 :].strip())
return branches
def _cleanup_local_branch_names(refs):
return [ref.strip() for ref in refs if not ref.endswith("HEAD")]
def _cleanup_tag_names(tag_names):
return [t.strip() for t in tag_names]