@@ -45,6 +45,22 @@ def confirm_tag_in_repo(tag, repo_name):
4545 return tag
4646
4747
48+ def find_rev_by_timestamp (timestamp , repo_name , refspec ):
49+ base_args = ["git" , "log" , "-1" , "--format=%H" ,
50+ '--before=' + timestamp ]
51+ # Prefer the most-recent change _made by swift-ci_ before the timestamp,
52+ # falling back to most-recent in general if there is none by swift-ci.
53+ rev = shell .capture (base_args + [ '--author' , 'swift-ci' , refspec ]).strip ()
54+ if rev :
55+ return rev
56+ rev = shell .capture (base_args + [refspec ]).strip ()
57+ if rev :
58+ return rev
59+ else :
60+ raise RuntimeError ('No rev in %s before timestamp %s' %
61+ (repo_name , timestamp ))
62+
63+
4864def get_branch_for_repo (config , repo_name , scheme_name , scheme_map ,
4965 cross_repos_pr ):
5066 cross_repo = False
@@ -68,8 +84,8 @@ def get_branch_for_repo(config, repo_name, scheme_name, scheme_map,
6884
6985
7086def update_single_repository (args ):
71- config , repo_name , scheme_name , scheme_map , tag , reset_to_remote , \
72- should_clean , cross_repos_pr = args
87+ config , repo_name , scheme_name , scheme_map , tag , timestamp , \
88+ reset_to_remote , should_clean , cross_repos_pr = args
7389 repo_path = os .path .join (SWIFT_SOURCE_ROOT , repo_name )
7490 if not os .path .isdir (repo_path ):
7591 return
@@ -84,6 +100,13 @@ def update_single_repository(args):
84100 elif scheme_name :
85101 checkout_target , cross_repo = get_branch_for_repo (
86102 config , repo_name , scheme_name , scheme_map , cross_repos_pr )
103+ if timestamp :
104+ checkout_target = find_rev_by_timestamp (timestamp ,
105+ repo_name ,
106+ checkout_target )
107+ elif timestamp :
108+ checkout_target = find_rev_by_timestamp (timestamp , repo_name ,
109+ "HEAD" )
87110
88111 # The clean option restores a repository to pristine condition.
89112 if should_clean :
@@ -153,6 +176,14 @@ def update_single_repository(args):
153176 return value
154177
155178
179+ def get_timestamp_to_match (args ):
180+ if not args .match_timestamp :
181+ return None
182+ with shell .pushd (os .path .join (SWIFT_SOURCE_ROOT , "swift" ),
183+ dry_run = False , echo = False ):
184+ return shell .capture (["git" , "log" , "-1" , "--format=%cI" ],
185+ echo = False ).strip ()
186+
156187def update_all_repositories (args , config , scheme_name , cross_repos_pr ):
157188 scheme_map = None
158189 if scheme_name :
@@ -165,6 +196,7 @@ def update_all_repositories(args, config, scheme_name, cross_repos_pr):
165196 scheme_map = v ['repos' ]
166197 break
167198 pool_args = []
199+ timestamp = get_timestamp_to_match (args )
168200 for repo_name in config ['repos' ].keys ():
169201 if repo_name in args .skip_repository_list :
170202 print ("Skipping update of '" + repo_name + "', requested by user" )
@@ -174,6 +206,7 @@ def update_all_repositories(args, config, scheme_name, cross_repos_pr):
174206 scheme_name ,
175207 scheme_map ,
176208 args .tag ,
209+ timestamp ,
177210 args .reset_to_remote ,
178211 args .clean ,
179212 cross_repos_pr ]
@@ -385,6 +418,11 @@ By default, updates your checkouts of Swift, SourceKit, LLDB, and SwiftPM.""")
385418 "--tag" ,
386419 help = """Check out each repository to the specified tag.""" ,
387420 metavar = 'TAG-NAME' )
421+ parser .add_argument (
422+ "--match-timestamp" ,
423+ help = 'Check out adjacent repositories to match timestamp of '
424+ ' current swift checkout.' ,
425+ action = 'store_true' )
388426 parser .add_argument (
389427 "-j" , "--jobs" ,
390428 type = int ,
0 commit comments