@@ -776,7 +776,7 @@ pub fn with_fetch_options(
776
776
777
777
pub fn fetch (
778
778
repo : & mut git2:: Repository ,
779
- url : & str ,
779
+ orig_url : & str ,
780
780
reference : & GitReference ,
781
781
config : & Config ,
782
782
) -> CargoResult < ( ) > {
@@ -792,7 +792,7 @@ pub fn fetch(
792
792
793
793
// If we're fetching from GitHub, attempt GitHub's special fast path for
794
794
// testing if we've already got an up-to-date copy of the repository
795
- let oid_to_fetch = match github_fast_path ( repo, url , reference, config) {
795
+ let oid_to_fetch = match github_fast_path ( repo, orig_url , reference, config) {
796
796
Ok ( FastPathRev :: UpToDate ) => return Ok ( ( ) ) ,
797
797
Ok ( FastPathRev :: NeedsFetch ( rev) ) => Some ( rev) ,
798
798
Ok ( FastPathRev :: Indeterminate ) => None ,
@@ -854,20 +854,19 @@ pub fn fetch(
854
854
// flavors of authentication possible while also still giving us all the
855
855
// speed and portability of using `libgit2`.
856
856
if let Some ( true ) = config. net_config ( ) ?. git_fetch_with_cli {
857
- return fetch_with_cli ( repo, url , & refspecs, tags, config) ;
857
+ return fetch_with_cli ( repo, orig_url , & refspecs, tags, config) ;
858
858
}
859
859
if config
860
860
. cli_unstable ( )
861
861
. gitoxide
862
862
. map_or ( false , |git| git. fetch )
863
863
{
864
- use git:: remote:: fetch:: Error ;
865
864
use git_repository as git;
866
865
let git2_repo = repo;
867
866
oxide:: with_retry_and_progress (
868
867
& git2_repo. path ( ) . to_owned ( ) ,
869
868
config,
870
- & mut |repo , should_interrupt, mut progress| {
869
+ & mut |repo_path , should_interrupt, mut progress, url_for_authentication | {
871
870
// The `fetch` operation here may fail spuriously due to a corrupt
872
871
// repository. It could also fail, however, for a whole slew of other
873
872
// reasons (aka network related reasons). We want Cargo to automatically
@@ -879,44 +878,93 @@ pub fn fetch(
879
878
// again. If it looks like any other kind of error, or if we've already
880
879
// blown away the repository, then we want to return the error as-is.
881
880
let mut repo_reinitialized = false ;
882
- let mut repo_storage;
883
- let mut repo = & * repo;
884
881
loop {
885
- debug ! ( "initiating fetch of {:?} from {}" , refspecs, url) ;
886
- let res = repo
887
- . remote_at ( url) ?
888
- . with_refspecs (
889
- refspecs. iter ( ) . map ( |s| s. as_str ( ) ) ,
890
- git:: remote:: Direction :: Fetch ,
891
- ) ?
892
- . connect ( git:: remote:: Direction :: Fetch , & mut progress) ?
893
- . prepare_fetch ( git:: remote:: ref_map:: Options :: default ( ) ) ?
894
- . receive ( should_interrupt) ;
882
+ let repo = oxide:: open_repo ( repo_path, config) ;
883
+ let res = match repo {
884
+ Ok ( repo) => {
885
+ debug ! ( "initiating fetch of {:?} from {}" , refspecs, orig_url) ;
886
+ ( {
887
+ let url_for_authentication = & mut * url_for_authentication;
888
+ || -> CargoResult < _ > {
889
+ let remote = repo. remote_at ( orig_url) ?. with_refspecs (
890
+ refspecs. iter ( ) . map ( |s| s. as_str ( ) ) ,
891
+ git:: remote:: Direction :: Fetch ,
892
+ ) ?;
893
+ let url = remote
894
+ . url ( git:: remote:: Direction :: Fetch )
895
+ . expect ( "set at init" )
896
+ . to_owned ( ) ;
897
+ let connection = remote
898
+ . connect ( git:: remote:: Direction :: Fetch , & mut progress) ?;
899
+ let mut authenticate =
900
+ connection. configured_credentials ( url) ?;
901
+ let connection = connection. with_credentials (
902
+ move |action : git:: protocol:: credentials:: helper:: Action | {
903
+ if let Some ( url) =
904
+ action. context ( ) . and_then ( |ctx| ctx. url . as_ref ( ) . filter ( |url| * url != orig_url) )
905
+ {
906
+ url_for_authentication ( url. as_ref ( ) ) ;
907
+ }
908
+ authenticate ( action)
909
+ } ,
910
+ ) ;
911
+ let outcome = connection
912
+ . prepare_fetch ( git:: remote:: ref_map:: Options :: default ( ) ) ?
913
+ . receive ( should_interrupt) ?;
914
+ Ok ( outcome)
915
+ }
916
+ } ) ( )
917
+ }
918
+ Err ( err) => Err ( err) ,
919
+ } ;
895
920
let err = match res {
896
921
Ok ( _) => break ,
897
922
Err ( e) => e,
898
923
} ;
899
924
debug ! ( "fetch failed: {}" , err) ;
900
925
901
926
if !repo_reinitialized
902
- && matches ! (
903
- err,
904
- Error :: Configuration { .. }
905
- | Error :: IncompatibleObjectHash { .. }
906
- | Error :: WritePack ( _)
907
- | Error :: UpdateRefs ( _)
908
- | Error :: RemovePackKeepFile { .. }
909
- )
927
+ // We check for errors that could occour if the configuration, refs or odb files are corrupted.
928
+ // We don't check for errors related to writing as `gitoxide` is expected to create missing leading
929
+ // folder before writing files into it, or else not even open a directory as git repository (which is
930
+ // also handled here).
931
+ && err. downcast_ref :: < git:: open:: Error > ( ) . map_or ( false , |err| {
932
+ use git:: open:: Error ;
933
+ matches ! (
934
+ err,
935
+ Error :: NotARepository { .. } | Error :: Config ( _)
936
+ )
937
+ } )
938
+ || err
939
+ . downcast_ref :: < git:: remote:: fetch:: prepare:: Error > ( )
940
+ . map_or ( false , |err| match err {
941
+ git:: remote:: fetch:: prepare:: Error :: RefMap ( err) => {
942
+ use git:: remote:: ref_map:: Error ;
943
+ matches ! (
944
+ err,
945
+ Error :: ListRefs ( _)
946
+ | Error :: GatherTransportConfig { .. }
947
+ | Error :: ConfigureCredentials ( _)
948
+ )
949
+ }
950
+ _ => false ,
951
+ } )
952
+ || err
953
+ . downcast_ref :: < git:: remote:: fetch:: Error > ( )
954
+ . map_or ( false , |err| {
955
+ use git:: remote:: fetch:: Error ;
956
+ matches ! (
957
+ err,
958
+ Error :: Configuration { .. } | Error :: RemovePackKeepFile { .. }
959
+ )
960
+ } )
910
961
{
911
962
repo_reinitialized = true ;
912
963
debug ! (
913
964
"looks like this is a corrupt repository, reinitializing \
914
965
and trying again"
915
966
) ;
916
967
if reinitialize ( git2_repo) . is_ok ( ) {
917
- repo_storage =
918
- git:: open_opts ( repo. path ( ) , repo. open_options ( ) . to_owned ( ) ) ?;
919
- repo = & repo_storage;
920
968
continue ;
921
969
}
922
970
}
@@ -927,9 +975,9 @@ pub fn fetch(
927
975
} ,
928
976
)
929
977
} else {
930
- debug ! ( "doing a fetch for {}" , url ) ;
978
+ debug ! ( "doing a fetch for {}" , orig_url ) ;
931
979
let git_config = git2:: Config :: open_default ( ) ?;
932
- with_fetch_options ( & git_config, url , config, & mut |mut opts| {
980
+ with_fetch_options ( & git_config, orig_url , config, & mut |mut opts| {
933
981
if tags {
934
982
opts. download_tags ( git2:: AutotagOption :: All ) ;
935
983
}
@@ -945,9 +993,9 @@ pub fn fetch(
945
993
// blown away the repository, then we want to return the error as-is.
946
994
let mut repo_reinitialized = false ;
947
995
loop {
948
- debug ! ( "initiating fetch of {:?} from {}" , refspecs, url ) ;
996
+ debug ! ( "initiating fetch of {:?} from {}" , refspecs, orig_url ) ;
949
997
let res = repo
950
- . remote_anonymous ( url ) ?
998
+ . remote_anonymous ( orig_url ) ?
951
999
. fetch ( & refspecs, Some ( & mut opts) , None ) ;
952
1000
let err = match res {
953
1001
Ok ( ( ) ) => break ,
0 commit comments