@@ -30,6 +30,11 @@ pub struct GitCommitRequest {
3030 pub message : String ,
3131}
3232
33+ #[ derive( Debug , Serialize , Deserialize , Clone ) ]
34+ pub struct GitRevertRequest {
35+ pub path : String ,
36+ }
37+
3338fn git_status_impl ( ) -> Result < Value > {
3439 let workdir = crate :: utils:: current_dir ( ) ;
3540
@@ -332,3 +337,56 @@ pub async fn handle_git_pull(ack: AckSender, _state: State<AppState>) {
332337 info ! ( "Received git:pull" ) ;
333338 send_response ( ack, git_pull_impl ( ) ) ;
334339}
340+
341+ fn git_revert_impl ( request : & GitRevertRequest ) -> Result < Value > {
342+ let workdir = crate :: utils:: current_dir ( ) ;
343+ let repo = Repository :: discover ( & workdir) ?;
344+ let repo_root = repo. workdir ( ) . unwrap_or ( Path :: new ( "." ) ) ;
345+
346+ // Convert to relative path
347+ let file_path = Path :: new ( & request. path ) ;
348+ let relative_path = if file_path. is_absolute ( ) {
349+ file_path. strip_prefix ( repo_root) . unwrap_or ( file_path)
350+ } else {
351+ file_path
352+ } ;
353+
354+ // Check file status to know if it's tracked or untracked
355+ let mut opts = StatusOptions :: new ( ) ;
356+ opts. include_untracked ( true )
357+ . pathspec ( & request. path ) ;
358+
359+ let statuses = repo. statuses ( Some ( & mut opts) ) ?;
360+ let is_new_file = statuses. iter ( ) . any ( |entry| {
361+ entry. status ( ) . contains ( Status :: WT_NEW ) ||
362+ entry. status ( ) . contains ( Status :: INDEX_NEW )
363+ } ) ;
364+
365+ if is_new_file {
366+ // For new/untracked files, just delete
367+ let full_path = repo_root. join ( relative_path) ;
368+ if full_path. exists ( ) {
369+ std:: fs:: remove_file ( & full_path)
370+ . context ( "Failed to delete untracked file" ) ?;
371+ }
372+ info ! ( "Git revert: deleted untracked file {}" , request. path) ;
373+ } else {
374+ // For tracked files, restore from HEAD
375+ let mut checkout_opts = git2:: build:: CheckoutBuilder :: new ( ) ;
376+ checkout_opts. path ( relative_path) . force ( ) ;
377+ repo. checkout_head ( Some ( & mut checkout_opts) )
378+ . context ( "Failed to restore file from HEAD" ) ?;
379+ info ! ( "Git revert: restored {} from HEAD" , request. path) ;
380+ }
381+
382+ Ok ( json ! ( { } ) )
383+ }
384+
385+ pub async fn handle_git_revert (
386+ Data ( request) : Data < GitRevertRequest > ,
387+ ack : AckSender ,
388+ _state : State < AppState > ,
389+ ) {
390+ info ! ( "Received git:revert: {:?}" , request. path) ;
391+ send_response ( ack, git_revert_impl ( & request) ) ;
392+ }
0 commit comments