@@ -358,10 +358,12 @@ impl AppCore {
358358 Ok ( conflicts) => ( conflicts. len ( ) , None ) ,
359359 Err ( error) => ( 0 , Some ( error) ) ,
360360 } ;
361+ let expected_trace2_target =
362+ format ! ( "af_unix:stream:{}" , self . paths. trace_socket. display( ) ) ;
363+ let trace2_target_overridden = settings. acceleration_enabled
364+ && git:: effective_trace2_target ( ) . ok ( ) . flatten ( ) . as_deref ( )
365+ != Some ( expected_trace2_target. as_str ( ) ) ;
361366 Ok ( AppSnapshot {
362- settings,
363- nodes,
364- routes,
365367 environment : EnvironmentSummary {
366368 git_available : git:: git_version ( ) . is_some ( ) ,
367369 git_path : git:: git_path ( ) ,
@@ -370,7 +372,11 @@ impl AppCore {
370372 config_path : self . paths . gitconfig . display ( ) . to_string ( ) ,
371373 conflicts,
372374 conflict_scan_error,
375+ trace2_target_overridden,
373376 } ,
377+ settings,
378+ nodes,
379+ routes,
374380 } )
375381 }
376382
@@ -907,7 +913,15 @@ impl AppCore {
907913 self . snapshot ( )
908914 }
909915
910- pub fn set_acceleration ( & self , enabled : bool ) -> Result < AppSnapshot , String > {
916+ pub fn trace2_target_conflict ( & self ) -> Result < Option < String > , String > {
917+ git:: external_trace2_target ( & self . paths . gitconfig )
918+ }
919+
920+ pub fn set_acceleration (
921+ & self ,
922+ enabled : bool ,
923+ replace_trace2_target : bool ,
924+ ) -> Result < AppSnapshot , String > {
911925 let _guard = self . lock . lock ( ) ;
912926 let mut settings = self . settings ( ) ?;
913927 if enabled && git:: git_version ( ) . is_none ( ) {
@@ -919,6 +933,12 @@ impl AppCore {
919933 if enabled && settings. route_scope == RouteScope :: Allowlist && self . routes ( ) ?. is_empty ( ) {
920934 return Err ( "仅加速清单为空,请先加入至少一个公开仓库" . into ( ) ) ;
921935 }
936+ if enabled
937+ && !replace_trace2_target
938+ && git:: external_trace2_target ( & self . paths . gitconfig ) ?. is_some ( )
939+ {
940+ return Err ( "检测到其他工具正在使用 Git Trace2,请在 GitBoost 窗口中选择保留现有配置或切换到 GitBoost" . into ( ) ) ;
941+ }
922942 if enabled {
923943 settings. acceleration_enabled = true ;
924944 } else {
@@ -1040,6 +1060,8 @@ impl AppCore {
10401060 git:: build_config ( settings, selected, routes, Some ( & self . paths . trace_socket ) ) ?;
10411061 let previous = fs:: read ( & self . paths . gitconfig ) . ok ( ) ;
10421062 let registered_before = git:: include_registered ( & self . paths . gitconfig ) ;
1063+ let preserve_external_trace = !settings. acceleration_enabled
1064+ && git:: external_trace2_target ( & self . paths . gitconfig ) ?. is_some ( ) ;
10431065 if settings. acceleration_enabled {
10441066 let mut candidate = NamedTempFile :: new_in ( & self . paths . root )
10451067 . map_err ( |error| format ! ( "无法创建配置候选文件:{error}" ) ) ?;
@@ -1063,7 +1085,17 @@ impl AppCore {
10631085 "gitboost.gitconfig" ,
10641086 ) ;
10651087 atomic_write ( & self . paths . gitconfig , content. as_bytes ( ) ) ?;
1066- if settings. acceleration_enabled || registered_before {
1088+ if preserve_external_trace {
1089+ if let Err ( error) = git:: unregister_include ( & self . paths . gitconfig ) {
1090+ self . restore_git_state ( previous. as_deref ( ) , registered_before) ;
1091+ return Err ( error) ;
1092+ }
1093+ } else if settings. acceleration_enabled {
1094+ if let Err ( error) = git:: register_include_last ( & self . paths . gitconfig ) {
1095+ self . restore_git_state ( previous. as_deref ( ) , registered_before) ;
1096+ return Err ( error) ;
1097+ }
1098+ } else if registered_before {
10671099 if let Err ( error) = git:: register_include ( & self . paths . gitconfig ) {
10681100 self . restore_git_state ( previous. as_deref ( ) , registered_before) ;
10691101 return Err ( error) ;
@@ -2805,7 +2837,7 @@ mod tests {
28052837 core. acknowledge_consent ( ) . unwrap ( ) ;
28062838
28072839 assert_eq ! (
2808- core. set_acceleration( true ) . unwrap_err( ) ,
2840+ core. set_acceleration( true , false ) . unwrap_err( ) ,
28092841 "没有通过真实 Git 检测的可用节点"
28102842 ) ;
28112843 let unchanged = core. settings ( ) . unwrap ( ) ;
@@ -2827,30 +2859,142 @@ mod tests {
28272859 )
28282860 . unwrap ( ) ;
28292861
2830- let automatic = core. set_acceleration ( true ) . unwrap ( ) ;
2862+ let automatic = core. set_acceleration ( true , false ) . unwrap ( ) ;
28312863 assert ! ( automatic. settings. acceleration_enabled) ;
28322864 assert_eq ! (
28332865 automatic. settings. current_node_id. as_deref( ) ,
28342866 Some ( FASTGIT_REWRITE_BASE )
28352867 ) ;
28362868
2837- let direct = core. set_acceleration ( false ) . unwrap ( ) ;
2869+ let direct = core. set_acceleration ( false , false ) . unwrap ( ) ;
28382870 assert ! ( !direct. settings. acceleration_enabled) ;
28392871 assert ! ( direct. settings. current_node_id. is_none( ) ) ;
28402872
2841- core. set_acceleration ( true ) . unwrap ( ) ;
2873+ core. set_acceleration ( true , false ) . unwrap ( ) ;
28422874 let route_id = core. routes ( ) . unwrap ( ) [ 0 ] . id . clone ( ) ;
28432875 let empty_allowlist = core. delete_route ( & route_id) . unwrap ( ) ;
28442876 assert ! ( !empty_allowlist. settings. acceleration_enabled) ;
28452877 assert ! ( empty_allowlist. settings. current_node_id. is_none( ) ) ;
28462878
28472879 core. set_route_scope ( RouteScope :: Global ) . unwrap ( ) ;
2848- core. set_acceleration ( true ) . unwrap ( ) ;
2880+ core. set_acceleration ( true , false ) . unwrap ( ) ;
28492881 let restored_allowlist = core. set_route_scope ( RouteScope :: Allowlist ) . unwrap ( ) ;
28502882 assert ! ( !restored_allowlist. settings. acceleration_enabled) ;
28512883 assert ! ( restored_allowlist. settings. current_node_id. is_none( ) ) ;
28522884 }
28532885
2886+ #[ test]
2887+ fn trace2_conflict_requires_a_choice_and_restores_the_external_target ( ) {
2888+ const MARKER : & str = "GITBOOST_TRACE2_CONFLICT_CHOICE_CHILD" ;
2889+ if rerun_with_isolated_git (
2890+ "core::tests::trace2_conflict_requires_a_choice_and_restores_the_external_target" ,
2891+ MARKER ,
2892+ ) {
2893+ return ;
2894+ }
2895+ let external_target = "af_unix:stream:/tmp/external-trace2.sock" ;
2896+ let configured = std:: process:: Command :: new ( "git" )
2897+ . args ( [ "config" , "--global" , "trace2.eventTarget" , external_target] )
2898+ . status ( )
2899+ . unwrap ( ) ;
2900+ assert ! ( configured. success( ) ) ;
2901+
2902+ let directory = tempfile:: tempdir ( ) . unwrap ( ) ;
2903+ let core = AppCore :: new ( directory. path ( ) . to_path_buf ( ) ) . unwrap ( ) ;
2904+ core. add_route ( "openai/codex" ) . unwrap ( ) ;
2905+ core. acknowledge_consent ( ) . unwrap ( ) ;
2906+ atomic_write_json (
2907+ & core. paths . health ,
2908+ & HashMap :: from ( [ (
2909+ FASTGIT_REWRITE_BASE . to_string ( ) ,
2910+ HealthSummary {
2911+ status : NodeStatus :: Available ,
2912+ in_auto_pool : true ,
2913+ success_count : 1 ,
2914+ attempt_count : 1 ,
2915+ ..HealthSummary :: default ( )
2916+ } ,
2917+ ) ] ) ,
2918+ )
2919+ . unwrap ( ) ;
2920+
2921+ assert_eq ! (
2922+ core. trace2_target_conflict( ) . unwrap( ) . as_deref( ) ,
2923+ Some ( external_target)
2924+ ) ;
2925+ assert ! ( core
2926+ . set_acceleration( true , false )
2927+ . unwrap_err( )
2928+ . contains( "Trace2" ) ) ;
2929+ assert ! ( !core. settings( ) . unwrap( ) . acceleration_enabled) ;
2930+
2931+ let enabled = core. set_acceleration ( true , true ) . unwrap ( ) ;
2932+ assert ! ( enabled. settings. acceleration_enabled) ;
2933+ assert ! ( enabled. environment. include_registered) ;
2934+ assert ! ( !enabled. environment. trace2_target_overridden) ;
2935+ let effective = std:: process:: Command :: new ( "git" )
2936+ . args ( [
2937+ "config" ,
2938+ "--global" ,
2939+ "--includes" ,
2940+ "--get" ,
2941+ "trace2.eventTarget" ,
2942+ ] )
2943+ . output ( )
2944+ . unwrap ( ) ;
2945+ assert ! ( effective. status. success( ) ) ;
2946+ assert_eq ! (
2947+ String :: from_utf8_lossy( & effective. stdout) . trim( ) ,
2948+ format!( "af_unix:stream:{}" , core. paths. trace_socket. display( ) )
2949+ ) ;
2950+
2951+ let external_config = directory. path ( ) . join ( "external.gitconfig" ) ;
2952+ fs:: write (
2953+ & external_config,
2954+ format ! ( "[trace2]\n \t eventTarget = {external_target}\n " ) ,
2955+ )
2956+ . unwrap ( ) ;
2957+ let overridden = std:: process:: Command :: new ( "git" )
2958+ . args ( [
2959+ "config" ,
2960+ "--global" ,
2961+ "--add" ,
2962+ "include.path" ,
2963+ external_config. to_string_lossy ( ) . as_ref ( ) ,
2964+ ] )
2965+ . status ( )
2966+ . unwrap ( ) ;
2967+ assert ! ( overridden. success( ) ) ;
2968+ assert ! (
2969+ core. snapshot( )
2970+ . unwrap( )
2971+ . environment
2972+ . trace2_target_overridden
2973+ ) ;
2974+
2975+ let reclaimed = core. set_acceleration ( true , true ) . unwrap ( ) ;
2976+ assert ! ( !reclaimed. environment. trace2_target_overridden) ;
2977+
2978+ let disabled = core. set_acceleration ( false , false ) . unwrap ( ) ;
2979+ assert ! ( !disabled. settings. acceleration_enabled) ;
2980+ assert ! ( !disabled. environment. include_registered) ;
2981+ let restored = std:: process:: Command :: new ( "git" )
2982+ . args ( [
2983+ "config" ,
2984+ "--global" ,
2985+ "--includes" ,
2986+ "--get" ,
2987+ "trace2.eventTarget" ,
2988+ ] )
2989+ . output ( )
2990+ . unwrap ( ) ;
2991+ assert ! ( restored. status. success( ) ) ;
2992+ assert_eq ! (
2993+ String :: from_utf8_lossy( & restored. stdout) . trim( ) ,
2994+ external_target
2995+ ) ;
2996+ }
2997+
28542998 #[ test]
28552999 fn acceleration_requires_privacy_consent_once ( ) {
28563000 const MARKER : & str = "GITBOOST_CONSENT_GATE_STATE_CHILD" ;
@@ -2867,7 +3011,7 @@ mod tests {
28673011 assert ! ( core. settings( ) . unwrap( ) . consent_acknowledged_at. is_none( ) ) ;
28683012
28693013 assert_eq ! (
2870- core. set_acceleration( true ) . unwrap_err( ) ,
3014+ core. set_acceleration( true , false ) . unwrap_err( ) ,
28713015 "首次开启加速前,请在 GitBoost 窗口中确认隐私提示"
28723016 ) ;
28733017 assert ! ( !core. settings( ) . unwrap( ) . acceleration_enabled) ;
@@ -2878,7 +3022,7 @@ mod tests {
28783022
28793023 // 同意后不再要求确认,错误回到线路本身
28803024 assert_eq ! (
2881- core. set_acceleration( true ) . unwrap_err( ) ,
3025+ core. set_acceleration( true , false ) . unwrap_err( ) ,
28823026 "没有通过真实 Git 检测的可用节点"
28833027 ) ;
28843028 }
@@ -3505,7 +3649,7 @@ mod tests {
35053649
35063650 core. add_route ( "openai/codex" ) . unwrap ( ) ;
35073651 assert_eq ! (
3508- core. set_acceleration( true ) . unwrap_err( ) ,
3652+ core. set_acceleration( true , false ) . unwrap_err( ) ,
35093653 "首次开启加速前,请在 GitBoost 窗口中确认隐私提示"
35103654 ) ;
35113655 core. acknowledge_consent ( ) . unwrap ( ) ;
@@ -3514,7 +3658,7 @@ mod tests {
35143658 // 真实文件持久化和重新加载后,同意状态仍然有效,不依赖进程内 mock 状态。
35153659 let core = AppCore :: new ( root. clone ( ) ) . unwrap ( ) ;
35163660 assert ! ( core. settings( ) . unwrap( ) . consent_acknowledged_at. is_some( ) ) ;
3517- let enabled = core. set_acceleration ( true ) . unwrap ( ) ;
3661+ let enabled = core. set_acceleration ( true , false ) . unwrap ( ) ;
35183662 assert ! ( enabled. settings. acceleration_enabled) ;
35193663 let node_id = enabled
35203664 . settings
0 commit comments