@@ -3,7 +3,7 @@ use std::env;
33use std:: ffi:: { CStr , CString , OsStr } ;
44use std:: iter:: IntoIterator ;
55use std:: mem;
6- use std:: path:: Path ;
6+ use std:: path:: { Path , PathBuf } ;
77use std:: ptr;
88use std:: str;
99
@@ -259,6 +259,33 @@ impl Repository {
259259 Repository :: open ( util:: bytes2path ( & * buf) )
260260 }
261261
262+ /// Attempt to find the path to a git repo for a given path
263+ ///
264+ /// This starts at `path` and looks up the filesystem hierarchy
265+ /// until it finds a repository, stopping if it finds a member of ceiling_dirs
266+ pub fn discover_path < P : AsRef < Path > , I , O > ( path : P , ceiling_dirs : I ) -> Result < PathBuf , Error >
267+ where
268+ O : AsRef < OsStr > ,
269+ I : IntoIterator < Item = O > ,
270+ {
271+ crate :: init ( ) ;
272+ let buf = Buf :: new ( ) ;
273+ // Normal file path OK (does not need Windows conversion).
274+ let path = path. as_ref ( ) . into_c_string ( ) ?;
275+ let ceiling_dirs_os = env:: join_paths ( ceiling_dirs) ?;
276+ let ceiling_dirs = ceiling_dirs_os. into_c_string ( ) ?;
277+ unsafe {
278+ try_call ! ( raw:: git_repository_discover(
279+ buf. raw( ) ,
280+ path,
281+ 1 ,
282+ ceiling_dirs
283+ ) ) ;
284+ }
285+
286+ Ok ( util:: bytes2path ( & * buf) . to_path_buf ( ) )
287+ }
288+
262289 /// Creates a new repository in the specified folder.
263290 ///
264291 /// This by default will create any necessary directories to create the
@@ -3428,6 +3455,34 @@ mod tests {
34283455 ) ;
34293456 }
34303457
3458+ #[ test]
3459+ fn smoke_discover_path ( ) {
3460+ let td = TempDir :: new ( ) . unwrap ( ) ;
3461+ let subdir = td. path ( ) . join ( "subdi" ) ;
3462+ fs:: create_dir ( & subdir) . unwrap ( ) ;
3463+ Repository :: init_bare ( td. path ( ) ) . unwrap ( ) ;
3464+ let path = Repository :: discover_path ( & subdir, & [ ] as & [ & OsStr ] ) . unwrap ( ) ;
3465+ assert_eq ! (
3466+ crate :: test:: realpath( & path) . unwrap( ) ,
3467+ crate :: test:: realpath( & td. path( ) . join( "" ) ) . unwrap( )
3468+ ) ;
3469+ }
3470+
3471+ #[ test]
3472+ fn smoke_discover_path_ceiling_dir ( ) {
3473+ let td = TempDir :: new ( ) . unwrap ( ) ;
3474+ let subdir = td. path ( ) . join ( "subdi" ) ;
3475+ fs:: create_dir ( & subdir) . unwrap ( ) ;
3476+ let ceilingdir = subdir. join ( "ceiling" ) ;
3477+ fs:: create_dir ( & ceilingdir) . unwrap ( ) ;
3478+ let testdir = ceilingdir. join ( "testdi" ) ;
3479+ fs:: create_dir ( & testdir) . unwrap ( ) ;
3480+ Repository :: init_bare ( td. path ( ) ) . unwrap ( ) ;
3481+ let path = Repository :: discover_path ( & testdir, & [ ceilingdir. as_os_str ( ) ] ) ;
3482+
3483+ assert ! ( path. is_err( ) ) ;
3484+ }
3485+
34313486 #[ test]
34323487 fn smoke_open_ext ( ) {
34333488 let td = TempDir :: new ( ) . unwrap ( ) ;
0 commit comments