@@ -26,7 +26,10 @@ use std::time::Duration;
2626use unicode_width:: UnicodeWidthStr ;
2727
2828use crate :: config:: Config ;
29- use crate :: constants:: { section_header, CONFIG_FILE_NAME , GIT_REMOTE_PREFIX , WORKTREES_SUBDIR } ;
29+ use crate :: constants:: {
30+ section_header, CONFIG_FILE_NAME , GIT_CRITICAL_DIRS , GIT_REMOTE_PREFIX , GIT_RESERVED_NAMES ,
31+ INVALID_FILESYSTEM_CHARS , MAX_WORKTREE_NAME_LENGTH , WINDOWS_RESERVED_CHARS , WORKTREES_SUBDIR ,
32+ } ;
3033use crate :: file_copy;
3134use crate :: git:: { GitWorktreeManager , WorktreeInfo } ;
3235use crate :: hooks:: { self , HookContext } ;
@@ -1847,20 +1850,19 @@ pub fn validate_worktree_name(name: &str) -> Result<String> {
18471850 }
18481851
18491852 // Check for invalid filesystem characters
1850- const INVALID_CHARS : & [ char ] = & [ '/' , '\\' , ':' , '*' , '?' , '"' , '<' , '>' , '|' , '\0' ] ;
1851- if name. chars ( ) . any ( |c| INVALID_CHARS . contains ( & c) ) {
1853+ if name. chars ( ) . any ( |c| INVALID_FILESYSTEM_CHARS . contains ( & c) ) {
18521854 return Err ( anyhow ! (
18531855 "Worktree name contains invalid characters: {}" ,
1854- INVALID_CHARS . iter( ) . collect:: <String >( )
1856+ INVALID_FILESYSTEM_CHARS . iter( ) . collect:: <String >( )
18551857 ) ) ;
18561858 }
18571859
18581860 // Check for names that could conflict with git internals (case insensitive)
1859- const RESERVED_NAMES : & [ & str ] = & [ ".git" , "HEAD" , "refs" , "hooks" , "info" , "objects" , "logs" ] ;
18601861 let name_lower = name. to_lowercase ( ) ;
1861- if RESERVED_NAMES
1862- . iter ( )
1863- . any ( |& reserved| reserved. to_lowercase ( ) == name_lower)
1862+ if name_lower == ".git"
1863+ || GIT_RESERVED_NAMES
1864+ . iter ( )
1865+ . any ( |& reserved| reserved. to_lowercase ( ) == name_lower)
18641866 {
18651867 return Err ( anyhow ! ( "Worktree name '{name}' is reserved by git" ) ) ;
18661868 }
@@ -1896,8 +1898,10 @@ pub fn validate_worktree_name(name: &str) -> Result<String> {
18961898 }
18971899
18981900 // Check for extremely long names
1899- if name. len ( ) > 255 {
1900- return Err ( anyhow ! ( "Worktree name is too long (max 255 characters)" ) ) ;
1901+ if name. len ( ) > MAX_WORKTREE_NAME_LENGTH {
1902+ return Err ( anyhow ! (
1903+ "Worktree name is too long (max {MAX_WORKTREE_NAME_LENGTH} characters)"
1904+ ) ) ;
19011905 }
19021906
19031907 Ok ( name. to_string ( ) )
@@ -1981,11 +1985,10 @@ pub fn validate_custom_path(path: &str) -> Result<()> {
19811985 }
19821986
19831987 // Check for Windows reserved characters (for cross-platform compatibility)
1984- const RESERVED_CHARS : & [ char ] = & [ '<' , '>' , ':' , '"' , '|' , '?' , '*' ] ;
1985- if path. chars ( ) . any ( |c| RESERVED_CHARS . contains ( & c) ) {
1988+ if path. chars ( ) . any ( |c| WINDOWS_RESERVED_CHARS . contains ( & c) ) {
19861989 return Err ( anyhow ! (
19871990 "Custom path contains reserved characters: {}" ,
1988- RESERVED_CHARS . iter( ) . collect:: <String >( )
1991+ WINDOWS_RESERVED_CHARS . iter( ) . collect:: <String >( )
19891992 ) ) ;
19901993 }
19911994
@@ -2015,15 +2018,34 @@ pub fn validate_custom_path(path: &str) -> Result<()> {
20152018
20162019 // Check for reserved names in path components
20172020 if let Some ( name_str) = name. to_str ( ) {
2018- const RESERVED_NAMES : & [ & str ] =
2019- & [ ".git" , "HEAD" , "refs" , "hooks" , "info" , "objects" , "logs" ] ;
20202021 let name_lower = name_str. to_lowercase ( ) ;
2021- if RESERVED_NAMES
2022+ if GIT_RESERVED_NAMES
20222023 . iter ( )
20232024 . any ( |& reserved| reserved. to_lowercase ( ) == name_lower)
20242025 {
20252026 return Err ( anyhow ! ( "Path component '{name_str}' is reserved by git" ) ) ;
20262027 }
2028+
2029+ // Special check for .git paths - only prevent critical Git metadata directories
2030+ if name_str == ".git" {
2031+ let components: Vec < _ > = path_obj. components ( ) . collect ( ) ;
2032+ if components. len ( ) > 1 {
2033+ // Check if trying to create in critical Git directories
2034+ if let Some ( std:: path:: Component :: Normal ( next) ) = components. get ( 1 ) {
2035+ if let Some ( next_str) = next. to_str ( ) {
2036+ // These directories contain critical Git metadata and should not be used for worktrees
2037+ if GIT_CRITICAL_DIRS . contains ( & next_str) {
2038+ return Err ( anyhow ! (
2039+ "Cannot create worktree in .git/{} directory. \
2040+ This directory contains critical Git metadata.",
2041+ next_str
2042+ ) ) ;
2043+ }
2044+ }
2045+ }
2046+ }
2047+ // Allow other .git subdirectories (like .git/worktrees-custom, etc.)
2048+ }
20272049 }
20282050 }
20292051 std:: path:: Component :: ParentDir => {
0 commit comments