@@ -53,13 +53,19 @@ pub fn run_cts(shell: Shell, mut args: Arguments) -> anyhow::Result<()> {
5353 if skip_checkout {
5454 bail ! ( "Skipping CTS checkout doesn't make sense when CTS is not present" ) ;
5555 }
56- log:: info!( "Cloning CTS" ) ;
57- shell
56+ let mut cmd = shell
5857 . cmd ( "git" )
5958 . args ( [ "clone" , CTS_GIT_URL , CTS_CHECKOUT_PATH ] )
60- . quiet ( )
61- . run ( )
62- . context ( "Failed to clone CTS" ) ?;
59+ . quiet ( ) ;
60+
61+ if git_version_at_least ( & shell, [ 2 , 49 , 0 ] ) ? {
62+ log:: info!( "Cloning CTS shallowly with revision {cts_revision}" ) ;
63+ cmd = cmd. args ( [ "--depth=1" , "--revision" , & cts_revision] )
64+ } else {
65+ log:: info!( "Cloning full checkout of CTS with revision {cts_revision}" ) ;
66+ }
67+
68+ cmd. run ( ) . context ( "Failed to clone CTS" ) ?;
6369
6470 shell. change_dir ( CTS_CHECKOUT_PATH ) ;
6571 } else if !skip_checkout {
@@ -120,3 +126,112 @@ pub fn run_cts(shell: Shell, mut args: Arguments) -> anyhow::Result<()> {
120126
121127 Ok ( ( ) )
122128}
129+
130+ fn git_version_at_least ( shell : & Shell , version : GitVersion ) -> anyhow:: Result < bool > {
131+ let output = shell
132+ . cmd ( "git" )
133+ . args ( [ "--version" ] )
134+ . output ( )
135+ . context ( "Failed to invoke `git --version`" ) ?;
136+
137+ let Some ( code) = output. status . code ( ) else {
138+ anyhow:: bail!( "`git --version` failed to return an exit code; interrupt via signal, maybe?" )
139+ } ;
140+
141+ anyhow:: ensure!( code == 0 , "`git --version` returned a nonzero exit code" ) ;
142+
143+ let fmt_err_msg = "`git --version` did not have the expected structure" ;
144+
145+ let stdout = String :: from_utf8 ( output. stdout ) . expect ( fmt_err_msg) ;
146+
147+ let parsed = parse_git_version_output ( & stdout) . expect ( fmt_err_msg) ;
148+
149+ Ok ( parsed <= version)
150+ }
151+
152+ pub type GitVersion = [ u8 ; 3 ] ;
153+
154+ fn parse_git_version_output ( output : & str ) -> anyhow:: Result < GitVersion > {
155+ const PREFIX : & str = "git version " ;
156+
157+ let raw_version = output
158+ . strip_prefix ( PREFIX )
159+ . with_context ( || format ! ( "missing `{PREFIX}` prefix" ) ) ?;
160+
161+ let raw_version = raw_version. trim_end ( ) ; // There should always be a newline at the end, but
162+ // we don't care if it's missing.
163+
164+ let parsed = GitVersion :: try_from (
165+ raw_version
166+ . splitn ( 3 , '.' )
167+ . enumerate ( )
168+ . map ( |( idx, s) | {
169+ s. parse ( ) . with_context ( || {
170+ format ! ( "failed to parse version number {idx} ({s:?}) as `u8`" )
171+ } )
172+ } )
173+ . collect :: < Result < Vec < _ > , _ > > ( ) ?,
174+ )
175+ . map_err ( |vec| anyhow:: Error :: msg ( format ! ( "less than 3 version numbers found: {vec:?}" ) ) ) ?;
176+
177+ log:: debug!( "detected Git version {raw_version}" ) ;
178+
179+ Ok ( parsed)
180+ }
181+
182+ #[ test]
183+ fn test_git_version_parsing ( ) {
184+ macro_rules! test_ok {
185+ ( $input: expr, $expected: expr) => {
186+ assert_eq!( parse_git_version_output( $input) . unwrap( ) , $expected) ;
187+ } ;
188+ }
189+ test_ok ! ( "git version 2.3.0" , [ 2 , 3 , 0 ] ) ;
190+ test_ok ! ( "git version 0.255.0" , [ 0 , 255 , 0 ] ) ;
191+ test_ok ! ( "git version 4.5.6" , [ 4 , 5 , 6 ] ) ;
192+
193+ macro_rules! test_err {
194+ ( $input: expr, $msg: expr) => {
195+ assert_eq!(
196+ parse_git_version_output( $input) . unwrap_err( ) . to_string( ) ,
197+ $msg
198+ )
199+ } ;
200+ }
201+ test_err ! ( "2.3.0" , "missing `git version ` prefix" ) ;
202+ test_err ! ( "" , "missing `git version ` prefix" ) ;
203+
204+ test_err ! (
205+ "git version 1.2" ,
206+ "less than 3 version numbers found: [1, 2]"
207+ ) ;
208+
209+ test_err ! (
210+ "git version 9001" ,
211+ "failed to parse version number 0 (\" 9001\" ) as `u8`"
212+ ) ;
213+ test_err ! (
214+ "git version " ,
215+ "failed to parse version number 0 (\" \" ) as `u8`"
216+ ) ;
217+ test_err ! (
218+ "git version asdf" ,
219+ "failed to parse version number 0 (\" asdf\" ) as `u8`"
220+ ) ;
221+ test_err ! (
222+ "git version 23.beta" ,
223+ "failed to parse version number 1 (\" beta\" ) as `u8`"
224+ ) ;
225+ test_err ! (
226+ "git version 1.2.wat" ,
227+ "failed to parse version number 2 (\" wat\" ) as `u8`"
228+ ) ;
229+ test_err ! (
230+ "git version 1.2.3." ,
231+ "failed to parse version number 2 (\" 3.\" ) as `u8`"
232+ ) ;
233+ test_err ! (
234+ "git version 1.2.3.4" ,
235+ "failed to parse version number 2 (\" 3.4\" ) as `u8`"
236+ ) ;
237+ }
0 commit comments