Skip to content

Commit fecbd22

Browse files
committed
utils/update-checkout: Rework for more parallelism in updates
Reworks the update_all_repositories() and update_single_repository() functions to benefit from more per-repo parallelism. * Branch-scheme search loop is relocated from update_repository_to_scheme() to update_all_repositories(). * Functions update_repository_to_tag() and update_repository_to_scheme() are removed. * Per-repo work of these functions is shifted to update_single_repository(). * Repeated work (repeated pushd's, etc.) has been eliminated. * As much work as possible is performed within update_single_repository()'s outer try clause to catch more errors. * Arguments passed to update_single_repository() have been changed appropriately. * New helper functions confirm_tag_in_repo() and get_branch_for_repo() added to keep update_single_repository()'s length under control. * Unnecessary setting of the cross_repo flag by the --tag argument has been removed, so repos which lack the tag are now properly rebased when updated.
1 parent bfa3cec commit fecbd22

File tree

1 file changed

+66
-73
lines changed

1 file changed

+66
-73
lines changed

utils/update-checkout

Lines changed: 66 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,56 @@ sys.path.append(os.path.join(SCRIPT_DIR, 'swift_build_support'))
3535
from swift_build_support import shell # noqa (E402)
3636

3737

38+
def confirm_tag_in_repo(tag, repo_name):
39+
tag_exists = shell.capture(['git', 'ls-remote', '--tags',
40+
'origin', tag], echo=False)
41+
if not tag_exists:
42+
print("Tag '" + tag + "' does not exist for '"
43+
+ repo_name + "', just updating regularly")
44+
tag = None
45+
return tag
46+
47+
48+
def get_branch_for_repo(config, repo_name, scheme_name, scheme_map,
49+
cross_repos_pr):
50+
cross_repo = False
51+
repo_branch = scheme_name
52+
if scheme_map:
53+
scheme_branch = scheme_map[repo_name]
54+
repo_branch = scheme_branch
55+
remote_repo_id = config['repos'][repo_name]['remote']['id']
56+
if remote_repo_id in cross_repos_pr:
57+
cross_repo = True
58+
pr_id = cross_repos_pr[remote_repo_id]
59+
repo_branch = "ci_pr_{0}".format(pr_id)
60+
shell.run(["git", "checkout", scheme_branch],
61+
echo=True)
62+
shell.capture(["git", "branch", "-D", repo_branch],
63+
echo=True, allow_non_zero_exit=True)
64+
shell.run(["git", "fetch", "origin",
65+
"pull/{0}/merge:{1}"
66+
.format(pr_id, repo_branch)], echo=True)
67+
return repo_branch, cross_repo
68+
69+
3870
def update_single_repository(args):
39-
repo_path, branch, reset_to_remote, should_clean, cross_repo = args
71+
config, repo_name, scheme_name, scheme_map, tag, reset_to_remote, \
72+
should_clean, cross_repos_pr = args
73+
repo_path = os.path.join(SWIFT_SOURCE_ROOT, repo_name)
4074
if not os.path.isdir(repo_path):
4175
return
4276

4377
try:
4478
print("Updating '" + repo_path + "'")
4579
with shell.pushd(repo_path, dry_run=False, echo=False):
80+
cross_repo = False
81+
checkout_target = None
82+
if tag:
83+
checkout_target = confirm_tag_in_repo(tag, repo_name)
84+
elif scheme_name:
85+
checkout_target, cross_repo = get_branch_for_repo(
86+
config, repo_name, scheme_name, scheme_map, cross_repos_pr)
87+
4688
# The clean option should restore a repository to pristine condition.
4789
if should_clean:
4890
shell.run(['git', 'clean', '-fdx'], echo=True)
@@ -57,10 +99,10 @@ def update_single_repository(args):
5799
except:
58100
pass
59101

60-
if branch:
102+
if checkout_target:
61103
shell.run(['git', 'status', '--porcelain', '-uno'],
62104
echo=False)
63-
shell.run(['git', 'checkout', branch], echo=True)
105+
shell.run(['git', 'checkout', checkout_target], echo=True)
64106

65107
# It's important that we checkout, fetch, and rebase, in order.
66108
# .git/FETCH_HEAD updates the not-for-merge attributes based on which
@@ -69,8 +111,8 @@ def update_single_repository(args):
69111

70112
# If we were asked to reset to the specified branch, do the hard
71113
# reset and return.
72-
if branch and reset_to_remote and not cross_repo:
73-
shell.run(['git', 'reset', '--hard', "origin/%s" % branch],
114+
if checkout_target and reset_to_remote and not cross_repo:
115+
shell.run(['git', 'reset', '--hard', "origin/%s" % checkout_target],
74116
echo=True)
75117
return
76118

@@ -110,80 +152,31 @@ def update_single_repository(args):
110152
return value
111153

112154

113-
def update_repository_to_tag(args, repo_name, repo_path, tag_name):
114-
with shell.pushd(repo_path, dry_run=False, echo=False):
115-
tag_exists = shell.capture(['git', 'ls-remote', '--tags',
116-
'origin', tag_name], echo=False)
117-
if not tag_exists:
118-
print("Tag '" + tag_name + "' does not exist for '"
119-
+ repo_name + "', just updating regularly")
120-
tag_name = None
121-
122-
return [repo_path,
123-
tag_name,
124-
args.reset_to_remote,
125-
args.clean,
126-
True]
127-
128-
129-
def update_repository_to_scheme(
130-
args, config, repo_name, repo_path, scheme_name, cross_repos_pr):
131-
cross_repo = False
132-
repo_branch = scheme_name
133-
# This loop is only correct, since we know that each alias set has
134-
# unique contents. This is checked by validate_config. Thus the first
135-
# branch scheme data that has scheme_name as one of its aliases is
136-
# the only possible correct answer.
137-
for v in config['branch-schemes'].values():
138-
if scheme_name not in v['aliases']:
139-
continue
140-
repo_branch = v['repos'][repo_name]
141-
remote_repo_id = config['repos'][repo_name]['remote']['id']
142-
if remote_repo_id in cross_repos_pr:
143-
cross_repo = True
144-
pr_id = cross_repos_pr[remote_repo_id]
145-
repo_branch = "ci_pr_{0}".format(pr_id)
146-
with shell.pushd(repo_path, dry_run=False, echo=False):
147-
shell.run(["git", "checkout", v['repos'][repo_name]],
148-
echo=True)
149-
shell.capture(["git", "branch", "-D", repo_branch],
150-
echo=True, allow_non_zero_exit=True)
151-
shell.run(["git", "fetch", "origin",
152-
"pull/{0}/merge:{1}"
153-
.format(pr_id, repo_branch)], echo=True)
154-
break
155-
return [repo_path,
156-
repo_branch,
157-
args.reset_to_remote,
158-
args.clean,
159-
cross_repo]
160-
161-
162155
def update_all_repositories(args, config, scheme_name, cross_repos_pr):
156+
scheme_map = None
157+
if scheme_name:
158+
# This loop is only correct, since we know that each alias set has
159+
# unique contents. This is checked by validate_config. Thus the first
160+
# branch scheme data that has scheme_name as one of its aliases is
161+
# the only possible correct answer.
162+
for v in config['branch-schemes'].values():
163+
if scheme_name in v['aliases']:
164+
scheme_map = v['repos']
165+
break
163166
pool_args = []
164167
for repo_name in config['repos'].keys():
165168
if repo_name in args.skip_repository_list:
166169
print("Skipping update of '" + repo_name + "', requested by user")
167170
continue
168-
repo_path = os.path.join(SWIFT_SOURCE_ROOT, repo_name)
169-
if args.tag:
170-
my_args = update_repository_to_tag(args, repo_name, repo_path, args.tag)
171-
pool_args.append(my_args)
172-
elif scheme_name:
173-
my_args = update_repository_to_scheme(args,
174-
config,
175-
repo_name,
176-
repo_path,
177-
scheme_name,
178-
cross_repos_pr)
179-
pool_args.append(my_args)
180-
else:
181-
my_args = [repo_path,
182-
None,
183-
args.reset_to_remote,
184-
args.clean,
185-
False]
186-
pool_args.append(my_args)
171+
my_args = [config,
172+
repo_name,
173+
scheme_name,
174+
scheme_map,
175+
args.tag,
176+
args.reset_to_remote,
177+
args.clean,
178+
cross_repos_pr]
179+
pool_args.append(my_args)
187180

188181
return shell.run_parallel(update_single_repository, pool_args, args.n_processes)
189182

0 commit comments

Comments
 (0)