@@ -290,15 +290,7 @@ fn main() {
290290 env_logger:: init ( ) ;
291291 let cli = Cli :: parse ( ) ;
292292
293- let result = match & cli. command {
294- Command :: Clone ( args) => handle_clone ( args) ,
295- Command :: Fetch ( args) => handle_fetch ( args) ,
296- Command :: Pull ( args) => handle_pull ( args) ,
297- Command :: Push ( args) => handle_push ( args) ,
298- Command :: Remote ( args) => handle_remote ( args) ,
299- Command :: Filter ( args) => handle_filter ( args) ,
300- Command :: Link ( args) => handle_link ( args) ,
301- } ;
293+ let result = run_command ( & cli) ;
302294
303295 if let Err ( e) = result {
304296 eprintln ! ( "Error: {e}" ) ;
@@ -311,16 +303,16 @@ fn main() {
311303 }
312304}
313305
314- /// Apply josh filtering to all remote refs and update local refs
315- fn apply_josh_filtering (
316- repo_path : & std :: path :: Path ,
317- filter : & str ,
318- remote_name : & str ,
319- ) -> anyhow :: Result < ( ) > {
320- // Use josh API directly instead of calling josh-filter binary
321- let filterobj = josh_core :: filter :: parse ( filter )
322- . map_err ( from_josh_err )
323- . context ( "Failed to parse filter" ) ? ;
306+ fn run_command ( cli : & Cli ) -> anyhow :: Result < ( ) > {
307+ // For clone, do the initial repo setup before creating transaction
308+ let repo_path = if let Command :: Clone ( args ) = & cli . command {
309+ // For clone, we're not in a git repo initially, so clone first and use that path
310+ clone_repo ( args ) ?
311+ } else {
312+ // For other commands, we need to be in a git repo
313+ let repo = git2 :: Repository :: open_from_env ( ) . context ( "Not in a git repository" ) ? ;
314+ repo . path ( ) . parent ( ) . unwrap ( ) . to_path_buf ( )
315+ } ;
324316
325317 josh_core:: cache_sled:: sled_load ( & repo_path. join ( ".git" ) )
326318 . map_err ( from_josh_err)
@@ -330,16 +322,40 @@ fn apply_josh_filtering(
330322 josh_core:: cache_stack:: CacheStack :: new ( )
331323 . with_backend ( josh_core:: cache_sled:: SledCacheBackend :: default ( ) )
332324 . with_backend (
333- josh_core:: cache_notes:: NotesCacheBackend :: new ( repo_path)
325+ josh_core:: cache_notes:: NotesCacheBackend :: new ( & repo_path)
334326 . map_err ( from_josh_err)
335327 . context ( "Failed to create NotesCacheBackend" ) ?,
336328 ) ,
337329 ) ;
338330
339- // Open Josh transaction
340- let transaction = josh_core:: cache:: TransactionContext :: new ( repo_path, cache. clone ( ) )
331+ // Create transaction using the known repo path
332+ let transaction = josh_core:: cache:: TransactionContext :: new ( & repo_path, cache. clone ( ) )
341333 . open ( None )
342- . map_err ( from_josh_err) ?;
334+ . map_err ( from_josh_err)
335+ . context ( "Failed TransactionContext::open" ) ?;
336+
337+ match & cli. command {
338+ Command :: Clone ( args) => handle_clone ( args, & transaction) ,
339+ Command :: Fetch ( args) => handle_fetch ( args, & transaction) ,
340+ Command :: Pull ( args) => handle_pull ( args, & transaction) ,
341+ Command :: Push ( args) => handle_push ( args, & transaction) ,
342+ Command :: Remote ( args) => handle_remote ( args, & transaction) ,
343+ Command :: Filter ( args) => handle_filter ( args, & transaction) ,
344+ Command :: Link ( args) => handle_link ( args, & transaction) ,
345+ }
346+ }
347+
348+ /// Apply josh filtering to all remote refs and update local refs
349+ fn apply_josh_filtering (
350+ transaction : & josh_core:: cache:: Transaction ,
351+ repo_path : & std:: path:: Path ,
352+ filter : & str ,
353+ remote_name : & str ,
354+ ) -> anyhow:: Result < ( ) > {
355+ // Use josh API directly instead of calling josh-filter binary
356+ let filterobj = josh_core:: filter:: parse ( filter)
357+ . map_err ( from_josh_err)
358+ . context ( "Failed to parse filter" ) ?;
343359
344360 let repo = transaction. repo ( ) ;
345361
@@ -419,7 +435,8 @@ fn to_absolute_remote_url(url: &str) -> anyhow::Result<String> {
419435 }
420436}
421437
422- fn handle_clone ( args : & CloneArgs ) -> anyhow:: Result < ( ) > {
438+ /// Initial clone setup: create directory, init repo, add remote (no transaction needed)
439+ fn clone_repo ( args : & CloneArgs ) -> anyhow:: Result < std:: path:: PathBuf > {
423440 // Use the provided output directory
424441 let output_dir = args. out . clone ( ) ;
425442
@@ -439,6 +456,16 @@ fn handle_clone(args: &CloneArgs) -> anyhow::Result<()> {
439456
440457 handle_remote_add_repo ( & remote_add_args, & output_dir) ?;
441458
459+ Ok ( output_dir)
460+ }
461+
462+ fn handle_clone (
463+ args : & CloneArgs ,
464+ transaction : & josh_core:: cache:: Transaction ,
465+ ) -> anyhow:: Result < ( ) > {
466+ // Get the repo path from the transaction
467+ let output_dir = transaction. repo ( ) . path ( ) . parent ( ) . unwrap ( ) ;
468+
442469 // Create FetchArgs from CloneArgs
443470 let fetch_args = FetchArgs {
444471 remote : "origin" . to_string ( ) ,
@@ -447,13 +474,13 @@ fn handle_clone(args: &CloneArgs) -> anyhow::Result<()> {
447474 } ;
448475
449476 // Use handle_fetch to do the actual fetching and filtering
450- handle_fetch_repo ( & fetch_args, & output_dir) ?;
477+ handle_fetch_repo ( & fetch_args, output_dir, transaction ) ?;
451478
452479 // Get the default branch name from the remote HEAD symref
453480 let default_branch = if args. branch == "HEAD" {
454481 // Read the remote HEAD symref to get the default branch
455482 let head_ref = "refs/remotes/origin/HEAD" . to_string ( ) ;
456- let repo = git2 :: Repository :: open ( & output_dir ) . context ( "Failed to open repository" ) ? ;
483+ let repo = transaction . repo ( ) ;
457484
458485 let head_reference = repo
459486 . find_reference ( & head_ref)
@@ -508,7 +535,7 @@ fn handle_clone(args: &CloneArgs) -> anyhow::Result<()> {
508535 Ok ( ( ) )
509536}
510537
511- fn handle_pull ( args : & PullArgs ) -> anyhow:: Result < ( ) > {
538+ fn handle_pull ( args : & PullArgs , transaction : & josh_core :: cache :: Transaction ) -> anyhow:: Result < ( ) > {
512539 // Check if we're in a git repository
513540 let repo = git2:: Repository :: open_from_env ( ) . context ( "Not in a git repository" ) ?;
514541 let repo_path = repo. path ( ) . parent ( ) . unwrap ( ) . to_path_buf ( ) ;
@@ -521,7 +548,7 @@ fn handle_pull(args: &PullArgs) -> anyhow::Result<()> {
521548 } ;
522549
523550 // Use handle_fetch to do the actual fetching and filtering
524- handle_fetch_repo ( & fetch_args, & repo_path) ?;
551+ handle_fetch_repo ( & fetch_args, & repo_path, transaction ) ?;
525552
526553 // Get current working directory for shell commands
527554 let current_dir = std:: env:: current_dir ( ) ?;
@@ -549,12 +576,15 @@ fn handle_pull(args: &PullArgs) -> anyhow::Result<()> {
549576 Ok ( ( ) )
550577}
551578
552- fn handle_fetch ( args : & FetchArgs ) -> anyhow:: Result < ( ) > {
579+ fn handle_fetch (
580+ args : & FetchArgs ,
581+ transaction : & josh_core:: cache:: Transaction ,
582+ ) -> anyhow:: Result < ( ) > {
553583 // Check if we're in a git repository
554584 let repo = git2:: Repository :: open_from_env ( ) . context ( "Not in a git repository" ) ?;
555585 let repo_path = repo. path ( ) . parent ( ) . unwrap ( ) . to_path_buf ( ) ;
556586
557- handle_fetch_repo ( args, & repo_path)
587+ handle_fetch_repo ( args, & repo_path, transaction )
558588}
559589
560590fn try_parse_symref ( remote : & str , output : & str ) -> Option < ( String , String ) > {
@@ -567,7 +597,11 @@ fn try_parse_symref(remote: &str, output: &str) -> Option<(String, String)> {
567597 Some ( ( default_branch. to_string ( ) , default_branch_ref) )
568598}
569599
570- fn handle_fetch_repo ( args : & FetchArgs , repo_path : & std:: path:: Path ) -> anyhow:: Result < ( ) > {
600+ fn handle_fetch_repo (
601+ args : & FetchArgs ,
602+ repo_path : & std:: path:: Path ,
603+ transaction : & josh_core:: cache:: Transaction ,
604+ ) -> anyhow:: Result < ( ) > {
571605 let repo = git2:: Repository :: open ( repo_path) . context ( "Failed to open repository" ) ?;
572606
573607 // Read the remote URL from josh-remote config
@@ -618,15 +652,15 @@ fn handle_fetch_repo(args: &FetchArgs, repo_path: &std::path::Path) -> anyhow::R
618652 remote : args. remote . clone ( ) ,
619653 } ;
620654
621- handle_filter_repo ( & filter_args, repo_path, false ) ?;
655+ handle_filter_repo ( & filter_args, repo_path, false , transaction ) ?;
622656
623657 // Note: fetch doesn't checkout, it just updates the refs
624658 eprintln ! ( "Fetched from remote: {}" , args. remote) ;
625659
626660 Ok ( ( ) )
627661}
628662
629- fn handle_push ( args : & PushArgs ) -> anyhow:: Result < ( ) > {
663+ fn handle_push ( args : & PushArgs , transaction : & josh_core :: cache :: Transaction ) -> anyhow:: Result < ( ) > {
630664 // Read filter from git config for the specific remote
631665 let repo = git2:: Repository :: open_from_env ( ) . context ( "Not in a git repository" ) ?;
632666 let repo_path = repo. path ( ) . parent ( ) . unwrap ( ) ;
@@ -643,28 +677,6 @@ fn handle_push(args: &PushArgs) -> anyhow::Result<()> {
643677 . map_err ( from_josh_err)
644678 . context ( "Failed to parse filter" ) ?;
645679
646- josh_core:: cache_sled:: sled_load ( repo_path)
647- . map_err ( from_josh_err)
648- . context ( "Failed to load sled cache" ) ?;
649-
650- let cache = std:: sync:: Arc :: new (
651- josh_core:: cache_stack:: CacheStack :: new ( )
652- . with_backend ( josh_core:: cache_sled:: SledCacheBackend :: default ( ) )
653- . with_backend (
654- josh_core:: cache_notes:: NotesCacheBackend :: new ( repo_path)
655- . map_err ( from_josh_err)
656- . context ( "Failed to create NotesCacheBackend" ) ?,
657- ) ,
658- ) ;
659-
660- // Open Josh transaction
661- let transaction = josh_core:: cache:: TransactionContext :: from_env ( cache. clone ( ) )
662- . map_err ( from_josh_err)
663- . context ( "Failed TransactionContext::from_env" ) ?
664- . open ( None )
665- . map_err ( from_josh_err)
666- . context ( "Failed TransactionContext::open" ) ?;
667-
668680 // Get the remote URL from josh-remote config
669681 let remote_url = config
670682 . get_string ( & format ! ( "josh-remote.{}.url" , args. remote) )
@@ -848,14 +860,17 @@ fn handle_push(args: &PushArgs) -> anyhow::Result<()> {
848860 Ok ( ( ) )
849861}
850862
851- fn handle_link ( args : & LinkArgs ) -> anyhow:: Result < ( ) > {
863+ fn handle_link ( args : & LinkArgs , transaction : & josh_core :: cache :: Transaction ) -> anyhow:: Result < ( ) > {
852864 match & args. command {
853- LinkCommand :: Add ( add_args) => handle_link_add ( add_args) ,
854- LinkCommand :: Fetch ( fetch_args) => handle_link_fetch ( fetch_args) ,
865+ LinkCommand :: Add ( add_args) => handle_link_add ( add_args, transaction ) ,
866+ LinkCommand :: Fetch ( fetch_args) => handle_link_fetch ( fetch_args, transaction ) ,
855867 }
856868}
857869
858- fn handle_link_add ( args : & LinkAddArgs ) -> anyhow:: Result < ( ) > {
870+ fn handle_link_add (
871+ args : & LinkAddArgs ,
872+ transaction : & josh_core:: cache:: Transaction ,
873+ ) -> anyhow:: Result < ( ) > {
859874 use josh_core:: filter:: tree;
860875 use josh_core:: { JoshLinkFile , Oid } ;
861876
@@ -962,30 +977,8 @@ fn handle_link_add(args: &LinkAddArgs) -> anyhow::Result<()> {
962977 . map_err ( from_josh_err)
963978 . context ( "Failed to parse :link filter" ) ?;
964979
965- // Load the cache and create transaction
966- let repo_path = repo. path ( ) . parent ( ) . unwrap ( ) ;
967-
968- josh_core:: cache_sled:: sled_load ( & repo_path) . unwrap ( ) ;
969- let cache = std:: sync:: Arc :: new (
970- josh_core:: cache_stack:: CacheStack :: new ( )
971- . with_backend ( josh_core:: cache_sled:: SledCacheBackend :: default ( ) )
972- . with_backend (
973- josh_core:: cache_notes:: NotesCacheBackend :: new ( & repo_path)
974- . map_err ( from_josh_err)
975- . context ( "Failed to create NotesCacheBackend" ) ?,
976- ) ,
977- ) ;
978-
979- // Open Josh transaction
980- let transaction = josh_core:: cache:: TransactionContext :: from_env ( cache. clone ( ) )
981- . map_err ( from_josh_err)
982- . context ( "Failed TransactionContext::from_env" ) ?
983- . open ( None )
984- . map_err ( from_josh_err)
985- . context ( "Failed TransactionContext::open" ) ?;
986-
987980 let filtered_commit = josh_core:: filter_commit (
988- & transaction,
981+ transaction,
989982 link_filter,
990983 new_commit,
991984 josh_core:: filter:: empty ( ) ,
@@ -1009,7 +1002,10 @@ fn handle_link_add(args: &LinkAddArgs) -> anyhow::Result<()> {
10091002 Ok ( ( ) )
10101003}
10111004
1012- fn handle_link_fetch ( args : & LinkFetchArgs ) -> anyhow:: Result < ( ) > {
1005+ fn handle_link_fetch (
1006+ args : & LinkFetchArgs ,
1007+ transaction : & josh_core:: cache:: Transaction ,
1008+ ) -> anyhow:: Result < ( ) > {
10131009 use josh_core:: filter:: tree;
10141010 use josh_core:: { JoshLinkFile , Oid } ;
10151011
@@ -1147,28 +1143,8 @@ fn handle_link_fetch(args: &LinkFetchArgs) -> anyhow::Result<()> {
11471143 . map_err ( from_josh_err)
11481144 . context ( "Failed to parse :link filter" ) ?;
11491145
1150- // Load the cache and create transaction
1151- let repo_path = repo. path ( ) . parent ( ) . unwrap ( ) ;
1152- josh_core:: cache_sled:: sled_load ( & repo_path) . unwrap ( ) ;
1153- let cache = std:: sync:: Arc :: new (
1154- josh_core:: cache_stack:: CacheStack :: new ( )
1155- . with_backend ( josh_core:: cache_sled:: SledCacheBackend :: default ( ) )
1156- . with_backend (
1157- josh_core:: cache_notes:: NotesCacheBackend :: new ( & repo_path)
1158- . map_err ( from_josh_err)
1159- . context ( "Failed to create NotesCacheBackend" ) ?,
1160- ) ,
1161- ) ;
1162-
1163- // Open Josh transaction
1164- let transaction = josh_core:: cache:: TransactionContext :: from_env ( cache. clone ( ) )
1165- . map_err ( from_josh_err)
1166- . context ( "Failed TransactionContext::from_env" ) ?
1167- . open ( None )
1168- . map_err ( from_josh_err)
1169- . context ( "Failed TransactionContext::open" ) ?;
11701146 let filtered_commit = josh_core:: filter_commit (
1171- & transaction,
1147+ transaction,
11721148 link_filter,
11731149 new_commit,
11741150 josh_core:: filter:: empty ( ) ,
@@ -1189,7 +1165,10 @@ fn handle_link_fetch(args: &LinkFetchArgs) -> anyhow::Result<()> {
11891165 Ok ( ( ) )
11901166}
11911167
1192- fn handle_remote ( args : & RemoteArgs ) -> anyhow:: Result < ( ) > {
1168+ fn handle_remote (
1169+ args : & RemoteArgs ,
1170+ _transaction : & josh_core:: cache:: Transaction ,
1171+ ) -> anyhow:: Result < ( ) > {
11931172 match & args. command {
11941173 RemoteCommand :: Add ( add_args) => handle_remote_add ( add_args) ,
11951174 }
@@ -1268,18 +1247,22 @@ fn handle_remote_add_repo(args: &RemoteAddArgs, repo_path: &std::path::Path) ->
12681247}
12691248
12701249/// Handle the `josh filter` command - apply filtering to existing refs without fetching
1271- fn handle_filter ( args : & FilterArgs ) -> anyhow:: Result < ( ) > {
1250+ fn handle_filter (
1251+ args : & FilterArgs ,
1252+ transaction : & josh_core:: cache:: Transaction ,
1253+ ) -> anyhow:: Result < ( ) > {
12721254 let repo = git2:: Repository :: open_from_env ( ) . context ( "Not in a git repository" ) ?;
12731255 let repo_path = repo. path ( ) . parent ( ) . unwrap ( ) . to_path_buf ( ) ;
12741256
1275- handle_filter_repo ( args, & repo_path, true )
1257+ handle_filter_repo ( args, & repo_path, true , transaction )
12761258}
12771259
12781260/// Internal filter function that can be called from other handlers
12791261fn handle_filter_repo (
12801262 args : & FilterArgs ,
12811263 repo_path : & std:: path:: Path ,
12821264 print_messages : bool ,
1265+ transaction : & josh_core:: cache:: Transaction ,
12831266) -> anyhow:: Result < ( ) > {
12841267 let repo = git2:: Repository :: open ( repo_path) . context ( "Failed to open repository" ) ?;
12851268
@@ -1296,7 +1279,7 @@ fn handle_filter_repo(
12961279 }
12971280
12981281 // Apply josh filtering (this is the same as in handle_fetch but without the git fetch step)
1299- apply_josh_filtering ( repo_path, & filter, & args. remote ) ?;
1282+ apply_josh_filtering ( transaction , repo_path, & filter, & args. remote ) ?;
13001283
13011284 if print_messages {
13021285 println ! ( "Applied filter to remote: {}" , args. remote) ;
0 commit comments