Struct git_repository::Remote

source ·
pub struct Remote<'repo> { /* private fields */ }
Expand description

A remote which represents a way to interact with hosts for remote clones of the parent repository.

Implementations§

Builder methods

Set the url to be used when pushing data to a remote.

Set the url to be used when pushing data to a remote, without applying rewrite rules in case these could be faulty, eliminating one failure mode.

Configure how tags should be handled when fetching from the remote.

Examples found in repository?
src/clone/fetch/mod.rs (line 92)
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
    pub fn fetch_only<P>(
        &mut self,
        progress: P,
        should_interrupt: &std::sync::atomic::AtomicBool,
    ) -> Result<(Repository, crate::remote::fetch::Outcome), Error>
    where
        P: crate::Progress,
        P::SubProgress: 'static,
    {
        use crate::remote;
        use crate::{bstr::ByteVec, remote::fetch::RefLogMessage};

        let repo = self
            .repo
            .as_mut()
            .expect("user error: multiple calls are allowed only until it succeeds");

        let remote_name = match self.remote_name.as_ref() {
            Some(name) => name.to_owned(),
            None => repo
                .config
                .resolved
                .string_by_key("clone.defaultRemoteName")
                .map(|n| remote::name::validated(n.into_owned()))
                .unwrap_or_else(|| Ok("origin".into()))?,
        };

        let mut remote = repo
            .remote_at(self.url.clone())?
            .with_refspecs(
                Some(format!("+refs/heads/*:refs/remotes/{remote_name}/*").as_str()),
                remote::Direction::Fetch,
            )
            .expect("valid static spec");
        let mut clone_fetch_tags = None;
        if let Some(f) = self.configure_remote.as_mut() {
            remote = f(remote).map_err(|err| Error::RemoteConfiguration(err))?;
        } else {
            clone_fetch_tags = remote::fetch::Tags::All.into();
        }

        let config = util::write_remote_to_local_config_file(&mut remote, remote_name.clone())?;

        // Now we are free to apply remote configuration we don't want to be written to disk.
        if let Some(fetch_tags) = clone_fetch_tags {
            remote = remote.with_fetch_tags(fetch_tags);
        }

        // Add HEAD after the remote was written to config, we need it to know what to checkout later, and assure
        // the ref that HEAD points to is present no matter what.
        let head_refspec = git_refspec::parse(
            format!("HEAD:refs/remotes/{remote_name}/HEAD").as_str().into(),
            git_refspec::parse::Operation::Fetch,
        )
        .expect("valid")
        .to_owned();
        let pending_pack: remote::fetch::Prepare<'_, '_, _, _> =
            remote.connect(remote::Direction::Fetch, progress)?.prepare_fetch({
                let mut opts = self.fetch_options.clone();
                if !opts.extra_refspecs.contains(&head_refspec) {
                    opts.extra_refspecs.push(head_refspec)
                }
                opts
            })?;
        if pending_pack.ref_map().object_hash != repo.object_hash() {
            unimplemented!("configure repository to expect a different object hash as advertised by the server")
        }
        let reflog_message = {
            let mut b = self.url.to_bstring();
            b.insert_str(0, "clone: from ");
            b
        };
        let outcome = pending_pack
            .with_write_packed_refs_only(true)
            .with_reflog_message(RefLogMessage::Override {
                message: reflog_message.clone(),
            })
            .receive(should_interrupt)?;

        util::replace_changed_local_config_file(repo, config);
        util::update_head(
            repo,
            &outcome.ref_map.remote_refs,
            reflog_message.as_ref(),
            remote_name.as_ref(),
        )?;

        Ok((self.repo.take().expect("still present"), outcome))
    }

Add specs as refspecs for direction to our list if they are unique, or ignore them otherwise.

Examples found in repository?
src/clone/fetch/mod.rs (lines 76-79)
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
    pub fn fetch_only<P>(
        &mut self,
        progress: P,
        should_interrupt: &std::sync::atomic::AtomicBool,
    ) -> Result<(Repository, crate::remote::fetch::Outcome), Error>
    where
        P: crate::Progress,
        P::SubProgress: 'static,
    {
        use crate::remote;
        use crate::{bstr::ByteVec, remote::fetch::RefLogMessage};

        let repo = self
            .repo
            .as_mut()
            .expect("user error: multiple calls are allowed only until it succeeds");

        let remote_name = match self.remote_name.as_ref() {
            Some(name) => name.to_owned(),
            None => repo
                .config
                .resolved
                .string_by_key("clone.defaultRemoteName")
                .map(|n| remote::name::validated(n.into_owned()))
                .unwrap_or_else(|| Ok("origin".into()))?,
        };

        let mut remote = repo
            .remote_at(self.url.clone())?
            .with_refspecs(
                Some(format!("+refs/heads/*:refs/remotes/{remote_name}/*").as_str()),
                remote::Direction::Fetch,
            )
            .expect("valid static spec");
        let mut clone_fetch_tags = None;
        if let Some(f) = self.configure_remote.as_mut() {
            remote = f(remote).map_err(|err| Error::RemoteConfiguration(err))?;
        } else {
            clone_fetch_tags = remote::fetch::Tags::All.into();
        }

        let config = util::write_remote_to_local_config_file(&mut remote, remote_name.clone())?;

        // Now we are free to apply remote configuration we don't want to be written to disk.
        if let Some(fetch_tags) = clone_fetch_tags {
            remote = remote.with_fetch_tags(fetch_tags);
        }

        // Add HEAD after the remote was written to config, we need it to know what to checkout later, and assure
        // the ref that HEAD points to is present no matter what.
        let head_refspec = git_refspec::parse(
            format!("HEAD:refs/remotes/{remote_name}/HEAD").as_str().into(),
            git_refspec::parse::Operation::Fetch,
        )
        .expect("valid")
        .to_owned();
        let pending_pack: remote::fetch::Prepare<'_, '_, _, _> =
            remote.connect(remote::Direction::Fetch, progress)?.prepare_fetch({
                let mut opts = self.fetch_options.clone();
                if !opts.extra_refspecs.contains(&head_refspec) {
                    opts.extra_refspecs.push(head_refspec)
                }
                opts
            })?;
        if pending_pack.ref_map().object_hash != repo.object_hash() {
            unimplemented!("configure repository to expect a different object hash as advertised by the server")
        }
        let reflog_message = {
            let mut b = self.url.to_bstring();
            b.insert_str(0, "clone: from ");
            b
        };
        let outcome = pending_pack
            .with_write_packed_refs_only(true)
            .with_reflog_message(RefLogMessage::Override {
                message: reflog_message.clone(),
            })
            .receive(should_interrupt)?;

        util::replace_changed_local_config_file(repo, config);
        util::update_head(
            repo,
            &outcome.ref_map.remote_refs,
            reflog_message.as_ref(),
            remote_name.as_ref(),
        )?;

        Ok((self.repo.take().expect("still present"), outcome))
    }

Establishing connections to remote hosts (without performing a git-handshake).

Available on crate features async-network-client or blocking-network-client only.

Create a new connection using transport to communicate, with progress to indicate changes.

Note that this method expects the transport to be created by the user, which would involve the url(). It’s meant to be used when async operation is needed with runtimes of the user’s choice.

Examples found in repository?
src/remote/connect.rs (line 85)
75
76
77
78
79
80
81
82
83
84
85
86
    pub async fn connect<P>(
        &self,
        direction: crate::remote::Direction,
        progress: P,
    ) -> Result<Connection<'_, 'repo, Box<dyn Transport + Send>, P>, Error>
    where
        P: Progress,
    {
        let (url, version) = self.sanitized_url_and_version(direction)?;
        let transport = git_protocol::transport::connect(url, version).await?;
        Ok(self.to_connection_with_transport(transport, progress))
    }
Available on (crate features async-network-client or blocking-network-client) and (crate features blocking-network-client or async-network-client-async-std) only.

Connect to the url suitable for direction and return a handle through which operations can be performed.

Note that the protocol.version configuration key affects the transport protocol used to connect, with 2 being the default.

The transport used for connection can be configured via transport_mut().configure() assuming the actually used transport is well known. If that’s not the case, the transport can be created by hand and passed to to_connection_with_transport().

Examples found in repository?
src/clone/fetch/mod.rs (line 104)
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
    pub fn fetch_only<P>(
        &mut self,
        progress: P,
        should_interrupt: &std::sync::atomic::AtomicBool,
    ) -> Result<(Repository, crate::remote::fetch::Outcome), Error>
    where
        P: crate::Progress,
        P::SubProgress: 'static,
    {
        use crate::remote;
        use crate::{bstr::ByteVec, remote::fetch::RefLogMessage};

        let repo = self
            .repo
            .as_mut()
            .expect("user error: multiple calls are allowed only until it succeeds");

        let remote_name = match self.remote_name.as_ref() {
            Some(name) => name.to_owned(),
            None => repo
                .config
                .resolved
                .string_by_key("clone.defaultRemoteName")
                .map(|n| remote::name::validated(n.into_owned()))
                .unwrap_or_else(|| Ok("origin".into()))?,
        };

        let mut remote = repo
            .remote_at(self.url.clone())?
            .with_refspecs(
                Some(format!("+refs/heads/*:refs/remotes/{remote_name}/*").as_str()),
                remote::Direction::Fetch,
            )
            .expect("valid static spec");
        let mut clone_fetch_tags = None;
        if let Some(f) = self.configure_remote.as_mut() {
            remote = f(remote).map_err(|err| Error::RemoteConfiguration(err))?;
        } else {
            clone_fetch_tags = remote::fetch::Tags::All.into();
        }

        let config = util::write_remote_to_local_config_file(&mut remote, remote_name.clone())?;

        // Now we are free to apply remote configuration we don't want to be written to disk.
        if let Some(fetch_tags) = clone_fetch_tags {
            remote = remote.with_fetch_tags(fetch_tags);
        }

        // Add HEAD after the remote was written to config, we need it to know what to checkout later, and assure
        // the ref that HEAD points to is present no matter what.
        let head_refspec = git_refspec::parse(
            format!("HEAD:refs/remotes/{remote_name}/HEAD").as_str().into(),
            git_refspec::parse::Operation::Fetch,
        )
        .expect("valid")
        .to_owned();
        let pending_pack: remote::fetch::Prepare<'_, '_, _, _> =
            remote.connect(remote::Direction::Fetch, progress)?.prepare_fetch({
                let mut opts = self.fetch_options.clone();
                if !opts.extra_refspecs.contains(&head_refspec) {
                    opts.extra_refspecs.push(head_refspec)
                }
                opts
            })?;
        if pending_pack.ref_map().object_hash != repo.object_hash() {
            unimplemented!("configure repository to expect a different object hash as advertised by the server")
        }
        let reflog_message = {
            let mut b = self.url.to_bstring();
            b.insert_str(0, "clone: from ");
            b
        };
        let outcome = pending_pack
            .with_write_packed_refs_only(true)
            .with_reflog_message(RefLogMessage::Override {
                message: reflog_message.clone(),
            })
            .receive(should_interrupt)?;

        util::replace_changed_local_config_file(repo, config);
        util::update_head(
            repo,
            &outcome.ref_map.remote_refs,
            reflog_message.as_ref(),
            remote_name.as_ref(),
        )?;

        Ok((self.repo.take().expect("still present"), outcome))
    }
Available on crate features async-network-client or blocking-network-client only.

Produce the sanitized URL and protocol version to use as obtained by querying the repository configuration.

This can be useful when using custom transports to allow additional configuration.

Examples found in repository?
src/remote/connect.rs (line 83)
75
76
77
78
79
80
81
82
83
84
85
86
    pub async fn connect<P>(
        &self,
        direction: crate::remote::Direction,
        progress: P,
    ) -> Result<Connection<'_, 'repo, Box<dyn Transport + Send>, P>, Error>
    where
        P: Progress,
    {
        let (url, version) = self.sanitized_url_and_version(direction)?;
        let transport = git_protocol::transport::connect(url, version).await?;
        Ok(self.to_connection_with_transport(transport, progress))
    }

Serialize into git-config.

Save ourselves to the given config if we are a named remote or fail otherwise.

Note that all sections named remote "<name>" will be cleared of all values we are about to write, and the last remote "<name>" section will be containing all relevant values so that reloading the remote from config would yield the same in-memory state.

Examples found in repository?
src/remote/save.rs (line 112)
104
105
106
107
108
109
110
111
112
113
114
115
116
    pub fn save_as_to(
        &mut self,
        name: impl Into<BString>,
        config: &mut git_config::File<'static>,
    ) -> Result<(), AsError> {
        let name = crate::remote::name::validated(name)?;
        let prev_name = self.name.take();
        self.name = Some(name.into());
        self.save_to(config).map_err(|err| {
            self.name = prev_name;
            err.into()
        })
    }

Forcefully set our name to name and write our state to config similar to save_to().

Note that this sets a name for anonymous remotes, but overwrites the name for those who were named before. If this name is different from the current one, the git configuration will still contain the previous name, and the caller should account for that.

Examples found in repository?
src/clone/fetch/util.rs (line 24)
15
16
17
18
19
20
21
22
23
24
25
26
27
pub fn write_remote_to_local_config_file(
    remote: &mut crate::Remote<'_>,
    remote_name: BString,
) -> Result<git_config::File<'static>, Error> {
    let mut metadata = git_config::file::Metadata::from(git_config::Source::Local);
    let config_path = remote.repo.git_dir().join("config");
    metadata.path = Some(config_path.clone());
    let mut config =
        git_config::File::from_paths_metadata(Some(metadata), Default::default())?.expect("one file to load");
    remote.save_as_to(remote_name, &mut config)?;
    std::fs::write(config_path, config.to_bstring())?;
    Ok(config)
}

Access

Return the name of this remote or None if it wasn’t persisted to disk yet.

Examples found in repository?
src/remote/save.rs (line 37)
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
    pub fn save_to(&self, config: &mut git_config::File<'static>) -> Result<(), Error> {
        fn as_key(name: &str) -> git_config::parse::section::Key<'_> {
            name.try_into().expect("valid")
        }
        let name = self.name().ok_or_else(|| Error::NameMissing {
            url: self
                .url
                .as_ref()
                .or(self.push_url.as_ref())
                .expect("one url is always set")
                .to_owned(),
        })?;
        if let Some(section_ids) = config.sections_and_ids_by_name("remote").map(|it| {
            it.filter_map(|(s, id)| (s.header().subsection_name() == Some(name.as_bstr())).then(|| id))
                .collect::<Vec<_>>()
        }) {
            let mut sections_to_remove = Vec::new();
            const KEYS_TO_REMOVE: &[&str] = &["url", "pushurl", "fetch", "push", "tagOpt"];
            for id in section_ids {
                let mut section = config.section_mut_by_id(id).expect("just queried");
                let was_empty = section.num_values() == 0;

                for key in KEYS_TO_REMOVE {
                    while section.remove(key).is_some() {}
                }

                let is_empty_after_deletions_of_values_to_be_written = section.num_values() == 0;
                if !was_empty && is_empty_after_deletions_of_values_to_be_written {
                    sections_to_remove.push(id);
                }
            }
            for id in sections_to_remove {
                config.remove_section_by_id(id);
            }
        }
        let mut section = config
            .section_mut_or_create_new("remote", Some(name.as_ref()))
            .expect("section name is validated and 'remote' is acceptable");
        if let Some(url) = self.url.as_ref() {
            section.push(as_key("url"), Some(url.to_bstring().as_ref()))
        }
        if let Some(url) = self.push_url.as_ref() {
            section.push(as_key("pushurl"), Some(url.to_bstring().as_ref()))
        }
        if self.fetch_tags != Default::default() {
            section.push(
                as_key("tagOpt"),
                BStr::new(match self.fetch_tags {
                    remote::fetch::Tags::All => "--tags",
                    remote::fetch::Tags::None => "--no-tags",
                    remote::fetch::Tags::Included => unreachable!("BUG: the default shouldn't be written and we try"),
                })
                .into(),
            )
        }
        for (key, spec) in self
            .fetch_specs
            .iter()
            .map(|spec| ("fetch", spec))
            .chain(self.push_specs.iter().map(|spec| ("push", spec)))
        {
            section.push(as_key(key), Some(spec.to_ref().to_bstring().as_ref()))
        }
        Ok(())
    }
More examples
Hide additional examples
src/remote/connection/ref_map.rs (line 203)
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
    async fn fetch_refs(
        &mut self,
        filter_by_prefix: bool,
        extra_parameters: Vec<(String, Option<String>)>,
        refspecs: &[git_refspec::RefSpec],
    ) -> Result<HandshakeWithRefs, Error> {
        let mut credentials_storage;
        let url = self.transport.to_url();
        let authenticate = match self.authenticate.as_mut() {
            Some(f) => f,
            None => {
                let url = self
                    .remote
                    .url(Direction::Fetch)
                    .map(ToOwned::to_owned)
                    .unwrap_or_else(|| git_url::parse(url.as_ref()).expect("valid URL to be provided by transport"));
                credentials_storage = self.configured_credentials(url)?;
                &mut credentials_storage
            }
        };

        if self.transport_options.is_none() {
            self.transport_options = self
                .remote
                .repo
                .transport_options(url.as_ref(), self.remote.name().map(|n| n.as_bstr()))
                .map_err(|err| Error::GatherTransportConfig {
                    source: err,
                    url: url.into_owned(),
                })?;
        }
        if let Some(config) = self.transport_options.as_ref() {
            self.transport.configure(&**config)?;
        }
        let mut outcome =
            git_protocol::fetch::handshake(&mut self.transport, authenticate, extra_parameters, &mut self.progress)
                .await?;
        let refs = match outcome.refs.take() {
            Some(refs) => refs,
            None => {
                let agent_feature = self.remote.repo.config.user_agent_tuple();
                git_protocol::ls_refs(
                    &mut self.transport,
                    &outcome.capabilities,
                    move |_capabilities, arguments, features| {
                        features.push(agent_feature);
                        if filter_by_prefix {
                            let mut seen = HashSet::new();
                            for spec in refspecs {
                                let spec = spec.to_ref();
                                if seen.insert(spec.instruction()) {
                                    let mut prefixes = Vec::with_capacity(1);
                                    spec.expand_prefixes(&mut prefixes);
                                    for mut prefix in prefixes {
                                        prefix.insert_str(0, "ref-prefix ");
                                        arguments.push(prefix);
                                    }
                                }
                            }
                        }
                        Ok(git_protocol::ls_refs::Action::Continue)
                    },
                    &mut self.progress,
                )
                .await?
            }
        };
        Ok(HandshakeWithRefs { outcome, refs })
    }

Return our repository reference.

Return the set of ref-specs used for direction, which may be empty, in order of occurrence in the configuration.

Examples found in repository?
src/remote/connection/fetch/mod.rs (line 123)
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
    pub async fn prepare_fetch(
        mut self,
        options: ref_map::Options,
    ) -> Result<Prepare<'remote, 'repo, T, P>, prepare::Error> {
        if self.remote.refspecs(remote::Direction::Fetch).is_empty() {
            return Err(prepare::Error::MissingRefSpecs);
        }
        let ref_map = self.ref_map_inner(options).await?;
        Ok(Prepare {
            con: Some(self),
            ref_map,
            dry_run: DryRun::No,
            reflog_message: None,
            write_packed_refs: WritePackedRefs::Never,
        })
    }
More examples
Hide additional examples
src/remote/connection/fetch/receive_pack.rs (line 120)
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
    pub async fn receive(mut self, should_interrupt: &AtomicBool) -> Result<Outcome, Error> {
        let mut con = self.con.take().expect("receive() can only be called once");

        let handshake = &self.ref_map.handshake;
        let protocol_version = handshake.server_protocol_version;

        let fetch = git_protocol::Command::Fetch;
        let progress = &mut con.progress;
        let repo = con.remote.repo;
        let fetch_features = {
            let mut f = fetch.default_features(protocol_version, &handshake.capabilities);
            f.push(repo.config.user_agent_tuple());
            f
        };

        git_protocol::fetch::Response::check_required_features(protocol_version, &fetch_features)?;
        let sideband_all = fetch_features.iter().any(|(n, _)| *n == "sideband-all");
        let mut arguments = git_protocol::fetch::Arguments::new(protocol_version, fetch_features);
        if matches!(con.remote.fetch_tags, crate::remote::fetch::Tags::Included) {
            if !arguments.can_use_include_tag() {
                unimplemented!("we expect servers to support 'include-tag', otherwise we have to implement another pass to fetch attached tags separately");
            }
            arguments.use_include_tag();
        }
        let mut previous_response = None::<git_protocol::fetch::Response>;
        let mut round = 1;

        if self.ref_map.object_hash != repo.object_hash() {
            return Err(Error::IncompatibleObjectHash {
                local: repo.object_hash(),
                remote: self.ref_map.object_hash,
            });
        }

        let reader = 'negotiation: loop {
            progress.step();
            progress.set_name(format!("negotiate (round {})", round));

            let is_done = match negotiate::one_round(
                negotiate::Algorithm::Naive,
                round,
                repo,
                &self.ref_map,
                con.remote.fetch_tags,
                &mut arguments,
                previous_response.as_ref(),
            ) {
                Ok(_) if arguments.is_empty() => {
                    git_protocol::indicate_end_of_interaction(&mut con.transport).await.ok();
                    let update_refs = refs::update(
                        repo,
                        self.reflog_message
                            .take()
                            .unwrap_or_else(|| RefLogMessage::Prefixed { action: "fetch".into() }),
                        &self.ref_map.mappings,
                        con.remote.refspecs(remote::Direction::Fetch),
                        &self.ref_map.extra_refspecs,
                        con.remote.fetch_tags,
                        self.dry_run,
                        self.write_packed_refs,
                    )?;
                    return Ok(Outcome {
                        ref_map: std::mem::take(&mut self.ref_map),
                        status: Status::NoPackReceived { update_refs },
                    });
                }
                Ok(is_done) => is_done,
                Err(err) => {
                    git_protocol::indicate_end_of_interaction(&mut con.transport).await.ok();
                    return Err(err.into());
                }
            };
            round += 1;
            let mut reader = arguments.send(&mut con.transport, is_done).await?;
            if sideband_all {
                setup_remote_progress(progress, &mut reader);
            }
            let response = git_protocol::fetch::Response::from_line_reader(protocol_version, &mut reader).await?;
            if response.has_pack() {
                progress.step();
                progress.set_name("receiving pack");
                if !sideband_all {
                    setup_remote_progress(progress, &mut reader);
                }
                break 'negotiation reader;
            } else {
                previous_response = Some(response);
            }
        };

        let options = git_pack::bundle::write::Options {
            thread_limit: config::index_threads(repo)?,
            index_version: config::pack_index_version(repo)?,
            iteration_mode: git_pack::data::input::Mode::Verify,
            object_hash: con.remote.repo.object_hash(),
        };

        let mut write_pack_bundle = if matches!(self.dry_run, fetch::DryRun::No) {
            Some(git_pack::Bundle::write_to_directory(
                #[cfg(feature = "async-network-client")]
                {
                    git_protocol::futures_lite::io::BlockOn::new(reader)
                },
                #[cfg(not(feature = "async-network-client"))]
                {
                    reader
                },
                Some(repo.objects.store_ref().path().join("pack")),
                con.progress,
                should_interrupt,
                Some(Box::new({
                    let repo = repo.clone();
                    move |oid, buf| repo.objects.find(oid, buf).ok()
                })),
                options,
            )?)
        } else {
            drop(reader);
            None
        };

        if matches!(protocol_version, git_protocol::transport::Protocol::V2) {
            git_protocol::indicate_end_of_interaction(&mut con.transport).await.ok();
        }

        let update_refs = refs::update(
            repo,
            self.reflog_message
                .take()
                .unwrap_or_else(|| RefLogMessage::Prefixed { action: "fetch".into() }),
            &self.ref_map.mappings,
            con.remote.refspecs(remote::Direction::Fetch),
            &self.ref_map.extra_refspecs,
            con.remote.fetch_tags,
            self.dry_run,
            self.write_packed_refs,
        )?;

        if let Some(bundle) = write_pack_bundle.as_mut() {
            if !update_refs.edits.is_empty() || bundle.index.num_objects == 0 {
                if let Some(path) = bundle.keep_path.take() {
                    std::fs::remove_file(&path).map_err(|err| Error::RemovePackKeepFile { path, source: err })?;
                }
            }
        }

        Ok(Outcome {
            ref_map: std::mem::take(&mut self.ref_map),
            status: match write_pack_bundle {
                Some(write_pack_bundle) => Status::Change {
                    write_pack_bundle,
                    update_refs,
                },
                None => Status::DryRun { update_refs },
            },
        })
    }

Return how we handle tags when fetching the remote.

Return the url used for the given direction with rewrites from url.<base>.insteadOf|pushInsteadOf, unless the instance was created with one of the _without_url_rewrite() methods. For pushing, this is the remote.<name>.pushUrl or the remote.<name>.url used for fetching, and for fetching it’s the remote.<name>.url. Note that it’s possible to only have the push url set, in which case there will be no way to fetch from the remote as the push-url isn’t used for that.

Examples found in repository?
src/remote/access.rs (line 43)
36
37
38
39
40
41
42
43
44
45
    pub fn url(&self, direction: remote::Direction) -> Option<&git_url::Url> {
        match direction {
            remote::Direction::Fetch => self.url_alias.as_ref().or(self.url.as_ref()),
            remote::Direction::Push => self
                .push_url_alias
                .as_ref()
                .or(self.push_url.as_ref())
                .or_else(|| self.url(remote::Direction::Fetch)),
        }
    }
More examples
Hide additional examples
src/remote/connect.rs (line 141)
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
    pub fn sanitized_url_and_version(
        &self,
        direction: crate::remote::Direction,
    ) -> Result<(git_url::Url, git_protocol::transport::Protocol), Error> {
        fn sanitize(mut url: git_url::Url) -> Result<git_url::Url, Error> {
            if url.scheme == git_url::Scheme::File {
                let mut dir = git_path::to_native_path_on_windows(url.path.as_ref());
                let kind = git_discover::is_git(dir.as_ref())
                    .or_else(|_| {
                        dir.to_mut().push(git_discover::DOT_GIT_DIR);
                        git_discover::is_git(dir.as_ref())
                    })
                    .map_err(|err| Error::FileUrl {
                        source: err.into(),
                        url: url.clone(),
                    })?;
                let (git_dir, _work_dir) = git_discover::repository::Path::from_dot_git_dir(
                    dir.clone().into_owned(),
                    kind,
                    std::env::current_dir()?,
                )
                .ok_or_else(|| Error::InvalidRemoteRepositoryPath {
                    directory: dir.into_owned(),
                })?
                .into_repository_and_work_tree_directories();
                url.path = git_path::into_bstr(git_dir).into_owned();
            }
            Ok(url)
        }

        use git_protocol::transport::Protocol;
        let version = self
            .repo
            .config
            .resolved
            .integer("protocol", None, "version")
            .unwrap_or(Ok(2))
            .map_err(|err| Error::UnknownProtocol { given: err.input })
            .and_then(|num| {
                Ok(match num {
                    1 => Protocol::V1,
                    2 => Protocol::V2,
                    num => {
                        return Err(Error::UnknownProtocol {
                            given: num.to_string().into(),
                        })
                    }
                })
            })?;

        let url = self.url(direction).ok_or(Error::MissingUrl { direction })?.to_owned();
        if !self.repo.config.url_scheme()?.allow(&url.scheme) {
            return Err(Error::ProtocolDenied {
                url: url.to_bstring(),
                scheme: url.scheme,
            });
        }
        Ok((sanitize(url)?, version))
    }
src/remote/connection/ref_map.rs (line 191)
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
    async fn fetch_refs(
        &mut self,
        filter_by_prefix: bool,
        extra_parameters: Vec<(String, Option<String>)>,
        refspecs: &[git_refspec::RefSpec],
    ) -> Result<HandshakeWithRefs, Error> {
        let mut credentials_storage;
        let url = self.transport.to_url();
        let authenticate = match self.authenticate.as_mut() {
            Some(f) => f,
            None => {
                let url = self
                    .remote
                    .url(Direction::Fetch)
                    .map(ToOwned::to_owned)
                    .unwrap_or_else(|| git_url::parse(url.as_ref()).expect("valid URL to be provided by transport"));
                credentials_storage = self.configured_credentials(url)?;
                &mut credentials_storage
            }
        };

        if self.transport_options.is_none() {
            self.transport_options = self
                .remote
                .repo
                .transport_options(url.as_ref(), self.remote.name().map(|n| n.as_bstr()))
                .map_err(|err| Error::GatherTransportConfig {
                    source: err,
                    url: url.into_owned(),
                })?;
        }
        if let Some(config) = self.transport_options.as_ref() {
            self.transport.configure(&**config)?;
        }
        let mut outcome =
            git_protocol::fetch::handshake(&mut self.transport, authenticate, extra_parameters, &mut self.progress)
                .await?;
        let refs = match outcome.refs.take() {
            Some(refs) => refs,
            None => {
                let agent_feature = self.remote.repo.config.user_agent_tuple();
                git_protocol::ls_refs(
                    &mut self.transport,
                    &outcome.capabilities,
                    move |_capabilities, arguments, features| {
                        features.push(agent_feature);
                        if filter_by_prefix {
                            let mut seen = HashSet::new();
                            for spec in refspecs {
                                let spec = spec.to_ref();
                                if seen.insert(spec.instruction()) {
                                    let mut prefixes = Vec::with_capacity(1);
                                    spec.expand_prefixes(&mut prefixes);
                                    for mut prefix in prefixes {
                                        prefix.insert_str(0, "ref-prefix ");
                                        arguments.push(prefix);
                                    }
                                }
                            }
                        }
                        Ok(git_protocol::ls_refs::Action::Continue)
                    },
                    &mut self.progress,
                )
                .await?
            }
        };
        Ok(HandshakeWithRefs { outcome, refs })
    }

Modification

Read url.<base>.insteadOf|pushInsteadOf configuration variables and apply them to our urls, changing them in place.

This happens only once, and one if them may be changed even when reporting an error. If both urls fail, only the first error (for fetch urls) is reported.

Replace all currently set refspecs, typically from configuration, with the given specs for direction, or None if one of the input specs could not be parsed.

Trait Implementations§

Returns a copy of the value. Read more
Performs copy-assignment from source. Read more
Formats the value using the given formatter. Read more
This method tests for self and other values to be equal, and is used by ==.
This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.

Auto Trait Implementations§

Blanket Implementations§

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The alignment of pointer.
The type for initializers.
Initializes a with the given initializer. Read more
Dereferences the given pointer. Read more
Mutably dereferences the given pointer. Read more
Drops the object pointed to by the given pointer. Read more
Should always be Self
The resulting type after obtaining ownership.
Creates owned data from borrowed data, usually by cloning. Read more
Uses borrowed data to replace owned data, usually by cloning. Read more
The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.