@@ -88,6 +88,37 @@ impl Drop for StatusGuard {
8888 }
8989}
9090
91+ /// RAII guard for git operations (push/pull/submodule) that set `git_op = true`.
92+ /// If the spawned task panics without sending `GitOpComplete` or `RefreshRepo`,
93+ /// the guard sends `RefreshRepo` to trigger a status query that clears `git_op`.
94+ struct GitOpGuard {
95+ id : RepoId ,
96+ tx : UnboundedSender < Action > ,
97+ completed : bool ,
98+ }
99+
100+ impl GitOpGuard {
101+ fn new ( id : RepoId , tx : UnboundedSender < Action > ) -> Self {
102+ Self {
103+ id,
104+ tx,
105+ completed : false ,
106+ }
107+ }
108+
109+ fn complete ( mut self ) {
110+ self . completed = true ;
111+ }
112+ }
113+
114+ impl Drop for GitOpGuard {
115+ fn drop ( & mut self ) {
116+ if !self . completed {
117+ let _ = self . tx . send ( Action :: RefreshRepo ( self . id . clone ( ) ) ) ;
118+ }
119+ }
120+ }
121+
91122pub ( crate ) struct App {
92123 config : Config ,
93124 should_quit : bool ,
@@ -656,13 +687,15 @@ impl App {
656687 let repo_id = id. clone ( ) ;
657688 let tx = self . action_tx . clone ( ) ;
658689 tokio:: task:: spawn_blocking ( move || {
690+ let guard = GitOpGuard :: new ( repo_id. clone ( ) , tx. clone ( ) ) ;
659691 let output = std:: process:: Command :: new ( "git" )
660692 . arg ( "-C" )
661693 . arg ( & path)
662694 . args ( & git_args)
663695 . output ( ) ;
664696 match output {
665697 Ok ( o) if o. status . success ( ) => {
698+ guard. complete ( ) ;
666699 let _ = tx. send ( Action :: GitOpComplete {
667700 id : repo_id,
668701 message : format ! (
@@ -672,6 +705,7 @@ impl App {
672705 } ) ;
673706 }
674707 Ok ( o) => {
708+ guard. complete ( ) ;
675709 let stderr = String :: from_utf8_lossy ( & o. stderr ) ;
676710 let first_line = stderr
677711 . lines ( )
@@ -686,6 +720,7 @@ impl App {
686720 let _ = tx. send ( Action :: RefreshRepo ( repo_id) ) ;
687721 }
688722 Err ( e) => {
723+ guard. complete ( ) ;
689724 let _ = tx. send ( Action :: Error ( format ! (
690725 "git {} failed: {}" ,
691726 git_args. join( " " ) ,
@@ -726,13 +761,15 @@ impl App {
726761 let repo_id = id. clone ( ) ;
727762 let tx = self . action_tx . clone ( ) ;
728763 tokio:: task:: spawn_blocking ( move || {
764+ let guard = GitOpGuard :: new ( repo_id. clone ( ) , tx. clone ( ) ) ;
729765 let output = std:: process:: Command :: new ( "git" )
730766 . arg ( "-C" )
731767 . arg ( & path)
732768 . args ( & git_args)
733769 . output ( ) ;
734770 match output {
735771 Ok ( o) if o. status . success ( ) => {
772+ guard. complete ( ) ;
736773 let _ = tx. send ( Action :: GitOpComplete {
737774 id : repo_id,
738775 message : format ! (
@@ -742,6 +779,7 @@ impl App {
742779 } ) ;
743780 }
744781 Ok ( o) => {
782+ guard. complete ( ) ;
745783 let stderr = String :: from_utf8_lossy ( & o. stderr ) ;
746784 let first_line = stderr
747785 . lines ( )
@@ -756,6 +794,7 @@ impl App {
756794 let _ = tx. send ( Action :: RefreshRepo ( repo_id) ) ;
757795 }
758796 Err ( e) => {
797+ guard. complete ( ) ;
759798 let _ = tx. send ( Action :: Error ( format ! (
760799 "git {} failed: {}" ,
761800 git_args. join( " " ) ,
0 commit comments