@@ -33,6 +33,15 @@ struct Revision {
3333 frozen : Option < String > ,
3434}
3535
36+ impl Revision {
37+ fn display_rev ( & self ) -> String {
38+ match & self . frozen {
39+ Some ( rev) => format ! ( "{rev}@{}" , self . rev) ,
40+ None => self . rev . clone ( ) ,
41+ }
42+ }
43+ }
44+
3645pub ( crate ) async fn auto_update (
3746 store : & Store ,
3847 config : Option < PathBuf > ,
@@ -55,6 +64,9 @@ pub(crate) async fn auto_update(
5564 let selectors = Selectors :: default ( ) ;
5665 let workspace = Workspace :: discover ( store, workspace_root, config, Some ( & selectors) , true ) ?;
5766
67+ // Parse `# frozen: <rev>` comments from config files to display old revs
68+ let frozen_revs = parse_frozen_comments ( & workspace) ;
69+
5870 // Collect repos and deduplicate by RemoteRepo
5971 #[ allow( clippy:: mutable_key_type) ]
6072 let mut repo_updates: FxHashMap < & RemoteRepo , Vec < RepoInfo > > = FxHashMap :: default ( ) ;
@@ -129,12 +141,16 @@ pub(crate) async fn auto_update(
129141 let is_changed = remote_repo. rev != new_rev. rev ;
130142
131143 if is_changed {
144+ let old = Revision {
145+ rev : remote_repo. rev . clone ( ) ,
146+ frozen : frozen_revs. get ( & remote_repo. rev ) . cloned ( ) ,
147+ } ;
132148 writeln ! (
133149 printer. stdout( ) ,
134150 "[{}] updating {} -> {}" ,
135151 remote_repo. repo. as_str( ) . cyan( ) ,
136- remote_repo . rev ,
137- new_rev. rev
152+ old . display_rev ( ) ,
153+ new_rev. display_rev ( )
138154 ) ?;
139155 } else {
140156 writeln ! (
@@ -475,6 +491,25 @@ async fn get_best_candidate_tag(repo: &Path, rev: &str, current_rev: &str) -> Re
475491 . with_context ( || format ! ( "No tags found for revision {rev}" ) )
476492}
477493
494+ /// Parse `# frozen: <rev>` comments from config files, returning a map from SHA to rev name.
495+ fn parse_frozen_comments ( workspace : & Workspace ) -> FxHashMap < String , String > {
496+ let rev_re = regex ! ( r#"rev[:=]\s*['"]?([a-f0-9]{40})['"]?\s*#\s*frozen:\s*(\S+)"# ) ;
497+ let mut map = FxHashMap :: default ( ) ;
498+
499+ for project in workspace. projects ( ) {
500+ let Ok ( content) = fs_err:: read_to_string ( project. config_file ( ) ) else {
501+ continue ;
502+ } ;
503+ for line in content. lines ( ) {
504+ if let Some ( caps) = rev_re. captures ( line) {
505+ map. insert ( caps[ 1 ] . to_string ( ) , caps[ 2 ] . to_string ( ) ) ;
506+ }
507+ }
508+ }
509+
510+ map
511+ }
512+
478513async fn write_new_config ( path : & Path , revisions : & [ Option < Revision > ] ) -> Result < ( ) > {
479514 let content = fs_err:: tokio:: read_to_string ( path) . await ?;
480515 let new_content = match path. extension ( ) {
0 commit comments