@@ -231,6 +231,8 @@ fn absolute_to_relative(cwd: impl AsRef<Path>, path: impl AsRef<Path>) -> Result
231231 if a == b {
232232 cwd_parts. next ( ) ;
233233 path_parts. next ( ) ;
234+ } else {
235+ break ;
234236 }
235237 }
236238
@@ -250,6 +252,14 @@ fn absolute_to_relative(cwd: impl AsRef<Path>, path: impl AsRef<Path>) -> Result
250252fn format_path ( cwd : impl AsRef < Path > , path : impl AsRef < Path > ) -> String {
251253 absolute_to_relative ( cwd, path. as_ref ( ) )
252254 . map ( |p| p. to_string_lossy ( ) . to_string ( ) )
255+ // If we have three consecutive ".." then it should probably just stay as an absolute path.
256+ . map ( |p| {
257+ if p. starts_with ( "../../.." ) {
258+ path. as_ref ( ) . to_string_lossy ( ) . to_string ( )
259+ } else {
260+ p
261+ }
262+ } )
253263 . unwrap_or ( path. as_ref ( ) . to_string_lossy ( ) . to_string ( ) )
254264}
255265
@@ -407,4 +417,28 @@ mod tests {
407417 "tilde should not expand when not the first component"
408418 ) ;
409419 }
420+
421+ #[ tokio:: test]
422+ async fn test_format_path ( ) {
423+ tracing_subscriber:: fmt:: init ( ) ;
424+
425+ async fn assert_paths ( cwd : & str , path : & str , expected : & str ) {
426+ let ctx = Context :: builder ( ) . with_test_home ( ) . await . unwrap ( ) . build_fake ( ) ;
427+ let fs = ctx. fs ( ) ;
428+ let cwd = sanitize_path_tool_arg ( & ctx, cwd) ;
429+ let path = sanitize_path_tool_arg ( & ctx, path) ;
430+ fs. create_dir_all ( & cwd) . await . unwrap ( ) ;
431+ fs. create_dir_all ( & path) . await . unwrap ( ) ;
432+ // Using `contains` since the chroot test directory will prefix the formatted path with a tmpdir
433+ // path.
434+ assert ! ( format_path( cwd, path) . contains( expected) ) ;
435+ }
436+ assert_paths ( "/Users/testuser/src" , "/Users/testuser/Downloads" , "../Downloads" ) . await ;
437+ assert_paths (
438+ "/Users/testuser/projects/MyProject/src" ,
439+ "/Volumes/projects/MyProject/src" ,
440+ "/Volumes/projects/MyProject/src" ,
441+ )
442+ . await ;
443+ }
410444}
0 commit comments