|
2 | 2 | //! authentication/cloning.
|
3 | 3 |
|
4 | 4 | use crate::core::GitReference;
|
| 5 | +use crate::sources::git::oxide; |
5 | 6 | use crate::util::errors::CargoResult;
|
6 | 7 | use crate::util::{human_readable_bytes, network, Config, IntoUrl, MetricsCounter, Progress};
|
7 | 8 | use anyhow::{anyhow, Context as _};
|
@@ -855,51 +856,123 @@ pub fn fetch(
|
855 | 856 | if let Some(true) = config.net_config()?.git_fetch_with_cli {
|
856 | 857 | return fetch_with_cli(repo, url, &refspecs, tags, config);
|
857 | 858 | }
|
858 |
| - |
859 |
| - debug!("doing a fetch for {}", url); |
860 |
| - let git_config = git2::Config::open_default()?; |
861 |
| - with_fetch_options(&git_config, url, config, &mut |mut opts| { |
862 |
| - if tags { |
863 |
| - opts.download_tags(git2::AutotagOption::All); |
864 |
| - } |
865 |
| - // The `fetch` operation here may fail spuriously due to a corrupt |
866 |
| - // repository. It could also fail, however, for a whole slew of other |
867 |
| - // reasons (aka network related reasons). We want Cargo to automatically |
868 |
| - // recover from corrupt repositories, but we don't want Cargo to stomp |
869 |
| - // over other legitimate errors. |
870 |
| - // |
871 |
| - // Consequently we save off the error of the `fetch` operation and if it |
872 |
| - // looks like a "corrupt repo" error then we blow away the repo and try |
873 |
| - // again. If it looks like any other kind of error, or if we've already |
874 |
| - // blown away the repository, then we want to return the error as-is. |
875 |
| - let mut repo_reinitialized = false; |
876 |
| - loop { |
877 |
| - debug!("initiating fetch of {:?} from {}", refspecs, url); |
878 |
| - let res = repo |
879 |
| - .remote_anonymous(url)? |
880 |
| - .fetch(&refspecs, Some(&mut opts), None); |
881 |
| - let err = match res { |
882 |
| - Ok(()) => break, |
883 |
| - Err(e) => e, |
884 |
| - }; |
885 |
| - debug!("fetch failed: {}", err); |
886 |
| - |
887 |
| - if !repo_reinitialized && matches!(err.class(), ErrorClass::Reference | ErrorClass::Odb) |
888 |
| - { |
889 |
| - repo_reinitialized = true; |
890 |
| - debug!( |
891 |
| - "looks like this is a corrupt repository, reinitializing \ |
| 859 | + if config |
| 860 | + .cli_unstable() |
| 861 | + .gitoxide |
| 862 | + .map_or(false, |git| git.fetch) |
| 863 | + { |
| 864 | + use git::remote::fetch::Error; |
| 865 | + use git_repository as git; |
| 866 | + let git2_repo = repo; |
| 867 | + oxide::with_retry_and_progress( |
| 868 | + &git2_repo.path().to_owned(), |
| 869 | + config, |
| 870 | + &mut |repo, should_interrupt, progress| { |
| 871 | + // The `fetch` operation here may fail spuriously due to a corrupt |
| 872 | + // repository. It could also fail, however, for a whole slew of other |
| 873 | + // reasons (aka network related reasons). We want Cargo to automatically |
| 874 | + // recover from corrupt repositories, but we don't want Cargo to stomp |
| 875 | + // over other legitimate errors. |
| 876 | + // |
| 877 | + // Consequently we save off the error of the `fetch` operation and if it |
| 878 | + // looks like a "corrupt repo" error then we blow away the repo and try |
| 879 | + // again. If it looks like any other kind of error, or if we've already |
| 880 | + // blown away the repository, then we want to return the error as-is. |
| 881 | + let mut repo_reinitialized = false; |
| 882 | + let mut repo_storage; |
| 883 | + let mut repo = &*repo; |
| 884 | + 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, progress.add_child("fetch"))? |
| 893 | + .prepare_fetch(git::remote::ref_map::Options::default())? |
| 894 | + .receive(should_interrupt); |
| 895 | + let err = match res { |
| 896 | + Ok(_) => break, |
| 897 | + Err(e) => e, |
| 898 | + }; |
| 899 | + debug!("fetch failed: {}", err); |
| 900 | + |
| 901 | + if !repo_reinitialized |
| 902 | + && matches!( |
| 903 | + err, |
| 904 | + Error::Configuration { .. } |
| 905 | + | Error::IncompatibleObjectHash { .. } |
| 906 | + | Error::WritePack(_) |
| 907 | + | Error::UpdateRefs(_) |
| 908 | + | Error::RemovePackKeepFile { .. } |
| 909 | + ) |
| 910 | + { |
| 911 | + repo_reinitialized = true; |
| 912 | + debug!( |
| 913 | + "looks like this is a corrupt repository, reinitializing \ |
892 | 914 | and trying again"
|
893 |
| - ); |
894 |
| - if reinitialize(repo).is_ok() { |
895 |
| - continue; |
| 915 | + ); |
| 916 | + 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 | + continue; |
| 921 | + } |
| 922 | + } |
| 923 | + |
| 924 | + return Err(err.into()); |
896 | 925 | }
|
| 926 | + Ok(()) |
| 927 | + }, |
| 928 | + ) |
| 929 | + } else { |
| 930 | + debug!("doing a fetch for {}", url); |
| 931 | + let git_config = git2::Config::open_default()?; |
| 932 | + with_fetch_options(&git_config, url, config, &mut |mut opts| { |
| 933 | + if tags { |
| 934 | + opts.download_tags(git2::AutotagOption::All); |
897 | 935 | }
|
| 936 | + // The `fetch` operation here may fail spuriously due to a corrupt |
| 937 | + // repository. It could also fail, however, for a whole slew of other |
| 938 | + // reasons (aka network related reasons). We want Cargo to automatically |
| 939 | + // recover from corrupt repositories, but we don't want Cargo to stomp |
| 940 | + // over other legitimate errors. |
| 941 | + // |
| 942 | + // Consequently we save off the error of the `fetch` operation and if it |
| 943 | + // looks like a "corrupt repo" error then we blow away the repo and try |
| 944 | + // again. If it looks like any other kind of error, or if we've already |
| 945 | + // blown away the repository, then we want to return the error as-is. |
| 946 | + let mut repo_reinitialized = false; |
| 947 | + loop { |
| 948 | + debug!("initiating fetch of {:?} from {}", refspecs, url); |
| 949 | + let res = repo |
| 950 | + .remote_anonymous(url)? |
| 951 | + .fetch(&refspecs, Some(&mut opts), None); |
| 952 | + let err = match res { |
| 953 | + Ok(()) => break, |
| 954 | + Err(e) => e, |
| 955 | + }; |
| 956 | + debug!("fetch failed: {}", err); |
| 957 | + |
| 958 | + if !repo_reinitialized |
| 959 | + && matches!(err.class(), ErrorClass::Reference | ErrorClass::Odb) |
| 960 | + { |
| 961 | + repo_reinitialized = true; |
| 962 | + debug!( |
| 963 | + "looks like this is a corrupt repository, reinitializing \ |
| 964 | + and trying again" |
| 965 | + ); |
| 966 | + if reinitialize(repo).is_ok() { |
| 967 | + continue; |
| 968 | + } |
| 969 | + } |
898 | 970 |
|
899 |
| - return Err(err.into()); |
900 |
| - } |
901 |
| - Ok(()) |
902 |
| - }) |
| 971 | + return Err(err.into()); |
| 972 | + } |
| 973 | + Ok(()) |
| 974 | + }) |
| 975 | + } |
903 | 976 | }
|
904 | 977 |
|
905 | 978 | fn fetch_with_cli(
|
|
0 commit comments