1- use std:: path:: { Path , PathBuf } ;
2- use serde:: { Deserialize , Serialize } ;
3- use serde_json:: { json, Value } ;
4- use git2:: { Repository , Status , StatusOptions } ;
51use anyhow:: { Context , Result } ;
2+ use git2:: { Repository , Status , StatusOptions } ;
3+ use serde:: { Deserialize , Serialize } ;
4+ use serde_json:: { Value , json} ;
5+ use std:: path:: { Path , PathBuf } ;
66use tracing:: info;
77
88#[ derive( Debug , Serialize , Deserialize , Clone , Copy , PartialEq , Eq ) ]
99#[ serde( rename_all = "lowercase" ) ]
1010pub enum FileStatus {
11- Modified , Added , Deleted , Renamed , Conflict ,
11+ Modified ,
12+ Added ,
13+ Deleted ,
14+ Renamed ,
15+ Conflict ,
1216}
1317
1418#[ derive( Debug , Serialize , Deserialize , Clone , PartialEq , Eq ) ]
@@ -106,8 +110,10 @@ impl GitManager {
106110 /// Get current git status
107111 pub fn status ( & self ) -> Result < GitStatus > {
108112 let repo = self . repo ( ) ?;
113+ let repo_root = repo. workdir ( ) . unwrap_or ( Path :: new ( "." ) ) ;
109114
110- let branch = repo. head ( )
115+ let branch = repo
116+ . head ( )
111117 . map ( |h| h. shorthand ( ) . unwrap_or ( "HEAD" ) . to_string ( ) )
112118 . unwrap_or_else ( |_| "HEAD" . to_string ( ) ) ;
113119
@@ -121,25 +127,38 @@ impl GitManager {
121127 let mut files: Vec < GitFileStatus > = Vec :: new ( ) ;
122128
123129 for entry in statuses. iter ( ) {
124- let path = entry. path ( ) . unwrap_or ( "" ) . to_string ( ) ;
130+ let relative_path = entry. path ( ) . unwrap_or ( "" ) ;
125131 let status = entry. status ( ) ;
126132
127- let file_status = if status. contains ( Status :: WT_NEW ) || status. contains ( Status :: INDEX_NEW ) {
133+ let file_status = if status. contains ( Status :: WT_NEW )
134+ || status. contains ( Status :: INDEX_NEW )
135+ {
128136 FileStatus :: Added
129- } else if status. contains ( Status :: WT_DELETED ) || status. contains ( Status :: INDEX_DELETED ) {
137+ } else if status. contains ( Status :: WT_DELETED ) || status. contains ( Status :: INDEX_DELETED )
138+ {
130139 FileStatus :: Deleted
131- } else if status. contains ( Status :: WT_MODIFIED ) || status. contains ( Status :: INDEX_MODIFIED ) {
140+ } else if status. contains ( Status :: WT_MODIFIED )
141+ || status. contains ( Status :: INDEX_MODIFIED )
142+ {
132143 FileStatus :: Modified
133- } else if status. contains ( Status :: WT_RENAMED ) || status. contains ( Status :: INDEX_RENAMED ) {
144+ } else if status. contains ( Status :: WT_RENAMED ) || status. contains ( Status :: INDEX_RENAMED )
145+ {
134146 FileStatus :: Renamed
135147 } else {
136148 continue ;
137149 } ;
138150
139- files. push ( GitFileStatus { path, status : file_status } ) ;
151+ files. push ( GitFileStatus {
152+ path : repo_root. join ( relative_path) . to_string_lossy ( ) . to_string ( ) ,
153+ status : file_status,
154+ } ) ;
140155 }
141156
142- info ! ( "Git status: {} files changed on branch {}" , files. len( ) , branch) ;
157+ info ! (
158+ "Git status: {} files changed on branch {}" ,
159+ files. len( ) ,
160+ branch
161+ ) ;
143162
144163 Ok ( GitStatus { files, branch } )
145164 }
@@ -152,7 +171,11 @@ impl GitManager {
152171 } ;
153172
154173 if self . status_cache != new_status {
155- info ! ( "Git status changed: {} files on branch {}" , new_status. files. len( ) , new_status. branch) ;
174+ info ! (
175+ "Git status changed: {} files on branch {}" ,
176+ new_status. files. len( ) ,
177+ new_status. branch
178+ ) ;
156179 self . status_cache = new_status. clone ( ) ;
157180 Some ( new_status)
158181 } else {
@@ -172,7 +195,8 @@ impl GitManager {
172195 let file_path = Path :: new ( path) ;
173196
174197 let relative_path = if file_path. is_absolute ( ) {
175- file_path. strip_prefix ( repo_path)
198+ file_path
199+ . strip_prefix ( repo_path)
176200 . map ( |p| p. to_string_lossy ( ) . to_string ( ) )
177201 . unwrap_or_else ( |_| path. to_string ( ) )
178202 } else {
@@ -183,15 +207,25 @@ impl GitManager {
183207 let entry = match tree. get_path ( Path :: new ( & relative_path) ) {
184208 Ok ( e) => e,
185209 Err ( _) => {
186- return Ok ( FileOriginal { content : String :: new ( ) , is_new : true } ) ;
210+ return Ok ( FileOriginal {
211+ content : String :: new ( ) ,
212+ is_new : true ,
213+ } ) ;
187214 }
188215 } ;
189216
190217 let blob = repo. find_blob ( entry. id ( ) ) ?;
191218 let content = std:: str:: from_utf8 ( blob. content ( ) ) ?. to_string ( ) ;
192219
193- info ! ( "Got original content for {}: {} bytes" , relative_path, content. len( ) ) ;
194- Ok ( FileOriginal { content, is_new : false } )
220+ info ! (
221+ "Got original content for {}: {} bytes" ,
222+ relative_path,
223+ content. len( )
224+ ) ;
225+ Ok ( FileOriginal {
226+ content,
227+ is_new : false ,
228+ } )
195229 }
196230
197231 /// Commit files
@@ -221,11 +255,12 @@ impl GitManager {
221255 let tree_id = index. write_tree ( ) ?;
222256 let tree = repo. find_tree ( tree_id) ?;
223257
224- let sig = repo. signature ( ) . or_else ( |_| {
225- git2 :: Signature :: now ( "Anycode User" , "user@anycode.dev" )
226- } ) ?;
258+ let sig = repo
259+ . signature ( )
260+ . or_else ( |_| git2 :: Signature :: now ( "Anycode User" , "user@anycode.dev" ) ) ?;
227261
228- let parents: Vec < git2:: Commit > = repo. head ( )
262+ let parents: Vec < git2:: Commit > = repo
263+ . head ( )
229264 . ok ( )
230265 . and_then ( |h| h. peel_to_commit ( ) . ok ( ) )
231266 . map ( |c| vec ! [ c] )
@@ -246,8 +281,7 @@ impl GitManager {
246281 let mut remote = repo. find_remote ( "origin" ) ?;
247282 let head = repo. head ( ) ?;
248283
249- let branch_name = head. shorthand ( )
250- . context ( "Detached HEAD state" ) ?;
284+ let branch_name = head. shorthand ( ) . context ( "Detached HEAD state" ) ?;
251285
252286 let refspec = format ! ( "refs/heads/{}:refs/heads/{}" , branch_name, branch_name) ;
253287
@@ -270,8 +304,7 @@ impl GitManager {
270304 let repo = self . repo ( ) ?;
271305 let mut remote = repo. find_remote ( "origin" ) ?;
272306 let head = repo. head ( ) ?;
273- let branch_name = head. shorthand ( )
274- . context ( "Detached HEAD state" ) ?;
307+ let branch_name = head. shorthand ( ) . context ( "Detached HEAD state" ) ?;
275308
276309 // Fetch from remote
277310 let mut callbacks = git2:: RemoteCallbacks :: new ( ) ;
@@ -299,9 +332,8 @@ impl GitManager {
299332 let mut reference = repo. find_reference ( & refname) ?;
300333 reference. set_target ( remote_commit. id ( ) , "Fast-forward pull" ) ?;
301334
302- let checkout_result = repo. checkout_head ( Some (
303- git2:: build:: CheckoutBuilder :: default ( ) . safe ( )
304- ) ) ;
335+ let checkout_result =
336+ repo. checkout_head ( Some ( git2:: build:: CheckoutBuilder :: default ( ) . safe ( ) ) ) ;
305337
306338 if let Err ( e) = checkout_result {
307339 reference. set_target ( head. target ( ) . unwrap ( ) , "Revert failed pull" ) ?;
@@ -318,18 +350,22 @@ impl GitManager {
318350 let mut index = repo. index ( ) ?;
319351
320352 if index. has_conflicts ( ) {
321- let conflicts: Vec < String > = index. conflicts ( ) ?
353+ let conflicts: Vec < String > = index
354+ . conflicts ( ) ?
322355 . filter_map ( |c| c. ok ( ) )
323356 . filter_map ( |c| c. our . or ( c. their ) . or ( c. ancestor ) )
324357 . filter_map ( |entry| String :: from_utf8 ( entry. path ) . ok ( ) )
325358 . collect ( ) ;
326359
327- let checkout_result = repo. checkout_index ( None , Some (
328- git2:: build:: CheckoutBuilder :: default ( )
329- . allow_conflicts ( true )
330- . conflict_style_merge ( true )
331- . safe ( )
332- ) ) ;
360+ let checkout_result = repo. checkout_index (
361+ None ,
362+ Some (
363+ git2:: build:: CheckoutBuilder :: default ( )
364+ . allow_conflicts ( true )
365+ . conflict_style_merge ( true )
366+ . safe ( ) ,
367+ ) ,
368+ ) ;
333369
334370 if let Err ( e) = checkout_result {
335371 repo. cleanup_state ( ) ?;
@@ -344,9 +380,9 @@ impl GitManager {
344380 let tree_id = index. write_tree ( ) ?;
345381 let tree = repo. find_tree ( tree_id) ?;
346382
347- let sig = repo. signature ( ) . or_else ( |_| {
348- git2 :: Signature :: now ( "Anycode User" , "user@anycode.dev" )
349- } ) ?;
383+ let sig = repo
384+ . signature ( )
385+ . or_else ( |_| git2 :: Signature :: now ( "Anycode User" , "user@anycode.dev" ) ) ?;
350386
351387 let local_commit = head. peel_to_commit ( ) ?;
352388 let remote_commit_obj = repo. find_commit ( remote_commit. id ( ) ) ?;
@@ -357,7 +393,7 @@ impl GitManager {
357393 & sig,
358394 & format ! ( "Merge remote-tracking branch 'origin/{}'" , branch_name) ,
359395 & tree,
360- & [ & local_commit, & remote_commit_obj]
396+ & [ & local_commit, & remote_commit_obj] ,
361397 ) ?;
362398
363399 repo. cleanup_state ( ) ?;
@@ -384,15 +420,13 @@ impl GitManager {
384420
385421 let statuses = repo. statuses ( Some ( & mut opts) ) ?;
386422 let is_new_file = statuses. iter ( ) . any ( |entry| {
387- entry. status ( ) . contains ( Status :: WT_NEW ) ||
388- entry. status ( ) . contains ( Status :: INDEX_NEW )
423+ entry. status ( ) . contains ( Status :: WT_NEW ) || entry. status ( ) . contains ( Status :: INDEX_NEW )
389424 } ) ;
390425
391426 if is_new_file {
392427 let full_path = repo_root. join ( relative_path) ;
393428 if full_path. exists ( ) {
394- std:: fs:: remove_file ( & full_path)
395- . context ( "Failed to delete untracked file" ) ?;
429+ std:: fs:: remove_file ( & full_path) . context ( "Failed to delete untracked file" ) ?;
396430 }
397431 info ! ( "Git revert: deleted untracked file {}" , path) ;
398432 } else {
0 commit comments