@@ -1007,156 +1007,11 @@ pub fn fetch(
10071007 }
10081008
10091009 if let Some ( true ) = gctx. net_config ( ) ?. git_fetch_with_cli {
1010- return fetch_with_cli ( repo, remote_url, & refspecs, tags, gctx) ;
1011- }
1012-
1013- if gctx. cli_unstable ( ) . gitoxide . map_or ( false , |git| git. fetch ) {
1014- let git2_repo = repo;
1015- let config_overrides = cargo_config_to_gitoxide_overrides ( gctx) ?;
1016- let repo_reinitialized = AtomicBool :: default ( ) ;
1017- let res = oxide:: with_retry_and_progress (
1018- & git2_repo. path ( ) . to_owned ( ) ,
1019- gctx,
1020- & |repo_path,
1021- should_interrupt,
1022- mut progress,
1023- url_for_authentication : & mut dyn FnMut ( & gix:: bstr:: BStr ) | {
1024- // The `fetch` operation here may fail spuriously due to a corrupt
1025- // repository. It could also fail, however, for a whole slew of other
1026- // reasons (aka network related reasons). We want Cargo to automatically
1027- // recover from corrupt repositories, but we don't want Cargo to stomp
1028- // over other legitimate errors.
1029- //
1030- // Consequently we save off the error of the `fetch` operation and if it
1031- // looks like a "corrupt repo" error then we blow away the repo and try
1032- // again. If it looks like any other kind of error, or if we've already
1033- // blown away the repository, then we want to return the error as-is.
1034- loop {
1035- let res = oxide:: open_repo (
1036- repo_path,
1037- config_overrides. clone ( ) ,
1038- oxide:: OpenMode :: ForFetch ,
1039- )
1040- . map_err ( crate :: sources:: git:: fetch:: Error :: from)
1041- . and_then ( |repo| {
1042- debug ! ( "initiating fetch of {refspecs:?} from {remote_url}" ) ;
1043- let url_for_authentication = & mut * url_for_authentication;
1044- let remote = repo
1045- . remote_at ( remote_url) ?
1046- . with_fetch_tags ( if tags {
1047- gix:: remote:: fetch:: Tags :: All
1048- } else {
1049- gix:: remote:: fetch:: Tags :: Included
1050- } )
1051- . with_refspecs (
1052- refspecs. iter ( ) . map ( |s| s. as_str ( ) ) ,
1053- gix:: remote:: Direction :: Fetch ,
1054- )
1055- . map_err ( crate :: sources:: git:: fetch:: Error :: Other ) ?;
1056- let url = remote
1057- . url ( gix:: remote:: Direction :: Fetch )
1058- . expect ( "set at init" )
1059- . to_owned ( ) ;
1060- let connection = remote. connect ( gix:: remote:: Direction :: Fetch ) ?;
1061- let mut authenticate = connection. configured_credentials ( url) ?;
1062- let connection = connection. with_credentials (
1063- move |action : gix:: protocol:: credentials:: helper:: Action | {
1064- if let Some ( url) = action. context ( ) . and_then ( |gctx| {
1065- gctx. url . as_ref ( ) . filter ( |url| * url != remote_url)
1066- } ) {
1067- url_for_authentication ( url. as_ref ( ) ) ;
1068- }
1069- authenticate ( action)
1070- } ,
1071- ) ;
1072- let outcome = connection
1073- . prepare_fetch ( & mut progress, gix:: remote:: ref_map:: Options :: default ( ) ) ?
1074- . with_shallow ( shallow. clone ( ) . into ( ) )
1075- . receive ( & mut progress, should_interrupt) ?;
1076- Ok ( outcome)
1077- } ) ;
1078- let err = match res {
1079- Ok ( _) => break ,
1080- Err ( e) => e,
1081- } ;
1082- debug ! ( "fetch failed: {}" , err) ;
1083-
1084- if !repo_reinitialized. load ( Ordering :: Relaxed )
1085- // We check for errors that could occur if the configuration, refs or odb files are corrupted.
1086- // We don't check for errors related to writing as `gitoxide` is expected to create missing leading
1087- // folder before writing files into it, or else not even open a directory as git repository (which is
1088- // also handled here).
1089- && err. is_corrupted ( )
1090- || has_shallow_lock_file ( & err)
1091- {
1092- repo_reinitialized. store ( true , Ordering :: Relaxed ) ;
1093- debug ! (
1094- "looks like this is a corrupt repository, reinitializing \
1095- and trying again"
1096- ) ;
1097- if oxide:: reinitialize ( repo_path) . is_ok ( ) {
1098- continue ;
1099- }
1100- }
1101-
1102- return Err ( err. into ( ) ) ;
1103- }
1104- Ok ( ( ) )
1105- } ,
1106- ) ;
1107- if repo_reinitialized. load ( Ordering :: Relaxed ) {
1108- * git2_repo = git2:: Repository :: open ( git2_repo. path ( ) ) ?;
1109- }
1110- res
1010+ fetch_with_cli ( repo, remote_url, & refspecs, tags, gctx)
1011+ } else if gctx. cli_unstable ( ) . gitoxide . map_or ( false , |git| git. fetch ) {
1012+ fetch_with_gitoxide ( repo, remote_url, refspecs, tags, shallow, gctx)
11111013 } else {
1112- debug ! ( "doing a fetch for {remote_url}" ) ;
1113- let git_config = git2:: Config :: open_default ( ) ?;
1114- with_fetch_options ( & git_config, remote_url, gctx, & mut |mut opts| {
1115- if tags {
1116- opts. download_tags ( git2:: AutotagOption :: All ) ;
1117- }
1118- if let gix:: remote:: fetch:: Shallow :: DepthAtRemote ( depth) = shallow {
1119- opts. depth ( 0i32 . saturating_add_unsigned ( depth. get ( ) ) ) ;
1120- }
1121- // The `fetch` operation here may fail spuriously due to a corrupt
1122- // repository. It could also fail, however, for a whole slew of other
1123- // reasons (aka network related reasons). We want Cargo to automatically
1124- // recover from corrupt repositories, but we don't want Cargo to stomp
1125- // over other legitimate errors.
1126- //
1127- // Consequently we save off the error of the `fetch` operation and if it
1128- // looks like a "corrupt repo" error then we blow away the repo and try
1129- // again. If it looks like any other kind of error, or if we've already
1130- // blown away the repository, then we want to return the error as-is.
1131- let mut repo_reinitialized = false ;
1132- loop {
1133- debug ! ( "initiating fetch of {refspecs:?} from {remote_url}" ) ;
1134- let res =
1135- repo. remote_anonymous ( remote_url) ?
1136- . fetch ( & refspecs, Some ( & mut opts) , None ) ;
1137- let err = match res {
1138- Ok ( ( ) ) => break ,
1139- Err ( e) => e,
1140- } ;
1141- debug ! ( "fetch failed: {}" , err) ;
1142-
1143- if !repo_reinitialized
1144- && matches ! ( err. class( ) , ErrorClass :: Reference | ErrorClass :: Odb )
1145- {
1146- repo_reinitialized = true ;
1147- debug ! (
1148- "looks like this is a corrupt repository, reinitializing \
1149- and trying again"
1150- ) ;
1151- if reinitialize ( repo) . is_ok ( ) {
1152- continue ;
1153- }
1154- }
1155-
1156- return Err ( err. into ( ) ) ;
1157- }
1158- Ok ( ( ) )
1159- } )
1014+ fetch_with_libgit2 ( repo, remote_url, refspecs, tags, shallow, gctx)
11601015 }
11611016}
11621017
@@ -1227,6 +1082,171 @@ fn fetch_with_cli(
12271082 Ok ( ( ) )
12281083}
12291084
1085+ fn fetch_with_gitoxide (
1086+ repo : & mut git2:: Repository ,
1087+ remote_url : & str ,
1088+ refspecs : Vec < String > ,
1089+ tags : bool ,
1090+ shallow : gix:: remote:: fetch:: Shallow ,
1091+ gctx : & GlobalContext ,
1092+ ) -> CargoResult < ( ) > {
1093+ let git2_repo = repo;
1094+ let config_overrides = cargo_config_to_gitoxide_overrides ( gctx) ?;
1095+ let repo_reinitialized = AtomicBool :: default ( ) ;
1096+ let res = oxide:: with_retry_and_progress (
1097+ & git2_repo. path ( ) . to_owned ( ) ,
1098+ gctx,
1099+ & |repo_path,
1100+ should_interrupt,
1101+ mut progress,
1102+ url_for_authentication : & mut dyn FnMut ( & gix:: bstr:: BStr ) | {
1103+ // The `fetch` operation here may fail spuriously due to a corrupt
1104+ // repository. It could also fail, however, for a whole slew of other
1105+ // reasons (aka network related reasons). We want Cargo to automatically
1106+ // recover from corrupt repositories, but we don't want Cargo to stomp
1107+ // over other legitimate errors.
1108+ //
1109+ // Consequently we save off the error of the `fetch` operation and if it
1110+ // looks like a "corrupt repo" error then we blow away the repo and try
1111+ // again. If it looks like any other kind of error, or if we've already
1112+ // blown away the repository, then we want to return the error as-is.
1113+ loop {
1114+ let res = oxide:: open_repo (
1115+ repo_path,
1116+ config_overrides. clone ( ) ,
1117+ oxide:: OpenMode :: ForFetch ,
1118+ )
1119+ . map_err ( crate :: sources:: git:: fetch:: Error :: from)
1120+ . and_then ( |repo| {
1121+ debug ! ( "initiating fetch of {refspecs:?} from {remote_url}" ) ;
1122+ let url_for_authentication = & mut * url_for_authentication;
1123+ let remote = repo
1124+ . remote_at ( remote_url) ?
1125+ . with_fetch_tags ( if tags {
1126+ gix:: remote:: fetch:: Tags :: All
1127+ } else {
1128+ gix:: remote:: fetch:: Tags :: Included
1129+ } )
1130+ . with_refspecs (
1131+ refspecs. iter ( ) . map ( |s| s. as_str ( ) ) ,
1132+ gix:: remote:: Direction :: Fetch ,
1133+ )
1134+ . map_err ( crate :: sources:: git:: fetch:: Error :: Other ) ?;
1135+ let url = remote
1136+ . url ( gix:: remote:: Direction :: Fetch )
1137+ . expect ( "set at init" )
1138+ . to_owned ( ) ;
1139+ let connection = remote. connect ( gix:: remote:: Direction :: Fetch ) ?;
1140+ let mut authenticate = connection. configured_credentials ( url) ?;
1141+ let connection = connection. with_credentials (
1142+ move |action : gix:: protocol:: credentials:: helper:: Action | {
1143+ if let Some ( url) = action
1144+ . context ( )
1145+ . and_then ( |gctx| gctx. url . as_ref ( ) . filter ( |url| * url != remote_url) )
1146+ {
1147+ url_for_authentication ( url. as_ref ( ) ) ;
1148+ }
1149+ authenticate ( action)
1150+ } ,
1151+ ) ;
1152+ let outcome = connection
1153+ . prepare_fetch ( & mut progress, gix:: remote:: ref_map:: Options :: default ( ) ) ?
1154+ . with_shallow ( shallow. clone ( ) . into ( ) )
1155+ . receive ( & mut progress, should_interrupt) ?;
1156+ Ok ( outcome)
1157+ } ) ;
1158+ let err = match res {
1159+ Ok ( _) => break ,
1160+ Err ( e) => e,
1161+ } ;
1162+ debug ! ( "fetch failed: {}" , err) ;
1163+
1164+ if !repo_reinitialized. load ( Ordering :: Relaxed )
1165+ // We check for errors that could occur if the configuration, refs or odb files are corrupted.
1166+ // We don't check for errors related to writing as `gitoxide` is expected to create missing leading
1167+ // folder before writing files into it, or else not even open a directory as git repository (which is
1168+ // also handled here).
1169+ && err. is_corrupted ( )
1170+ || has_shallow_lock_file ( & err)
1171+ {
1172+ repo_reinitialized. store ( true , Ordering :: Relaxed ) ;
1173+ debug ! (
1174+ "looks like this is a corrupt repository, reinitializing \
1175+ and trying again"
1176+ ) ;
1177+ if oxide:: reinitialize ( repo_path) . is_ok ( ) {
1178+ continue ;
1179+ }
1180+ }
1181+
1182+ return Err ( err. into ( ) ) ;
1183+ }
1184+ Ok ( ( ) )
1185+ } ,
1186+ ) ;
1187+ if repo_reinitialized. load ( Ordering :: Relaxed ) {
1188+ * git2_repo = git2:: Repository :: open ( git2_repo. path ( ) ) ?;
1189+ }
1190+ res
1191+ }
1192+
1193+ fn fetch_with_libgit2 (
1194+ repo : & mut git2:: Repository ,
1195+ remote_url : & str ,
1196+ refspecs : Vec < String > ,
1197+ tags : bool ,
1198+ shallow : gix:: remote:: fetch:: Shallow ,
1199+ gctx : & GlobalContext ,
1200+ ) -> CargoResult < ( ) > {
1201+ debug ! ( "doing a fetch for {remote_url}" ) ;
1202+ let git_config = git2:: Config :: open_default ( ) ?;
1203+ with_fetch_options ( & git_config, remote_url, gctx, & mut |mut opts| {
1204+ if tags {
1205+ opts. download_tags ( git2:: AutotagOption :: All ) ;
1206+ }
1207+ if let gix:: remote:: fetch:: Shallow :: DepthAtRemote ( depth) = shallow {
1208+ opts. depth ( 0i32 . saturating_add_unsigned ( depth. get ( ) ) ) ;
1209+ }
1210+ // The `fetch` operation here may fail spuriously due to a corrupt
1211+ // repository. It could also fail, however, for a whole slew of other
1212+ // reasons (aka network related reasons). We want Cargo to automatically
1213+ // recover from corrupt repositories, but we don't want Cargo to stomp
1214+ // over other legitimate errors.
1215+ //
1216+ // Consequently we save off the error of the `fetch` operation and if it
1217+ // looks like a "corrupt repo" error then we blow away the repo and try
1218+ // again. If it looks like any other kind of error, or if we've already
1219+ // blown away the repository, then we want to return the error as-is.
1220+ let mut repo_reinitialized = false ;
1221+ loop {
1222+ debug ! ( "initiating fetch of {refspecs:?} from {remote_url}" ) ;
1223+ let res = repo
1224+ . remote_anonymous ( remote_url) ?
1225+ . fetch ( & refspecs, Some ( & mut opts) , None ) ;
1226+ let err = match res {
1227+ Ok ( ( ) ) => break ,
1228+ Err ( e) => e,
1229+ } ;
1230+ debug ! ( "fetch failed: {}" , err) ;
1231+
1232+ if !repo_reinitialized && matches ! ( err. class( ) , ErrorClass :: Reference | ErrorClass :: Odb )
1233+ {
1234+ repo_reinitialized = true ;
1235+ debug ! (
1236+ "looks like this is a corrupt repository, reinitializing \
1237+ and trying again"
1238+ ) ;
1239+ if reinitialize ( repo) . is_ok ( ) {
1240+ continue ;
1241+ }
1242+ }
1243+
1244+ return Err ( err. into ( ) ) ;
1245+ }
1246+ Ok ( ( ) )
1247+ } )
1248+ }
1249+
12301250/// Attempts to `git gc` a repository.
12311251///
12321252/// Cargo has a bunch of long-lived git repositories in its global cache and
0 commit comments