Skip to content

Commit 709a732

Browse files
committed
feat: Support for user-costomizable user agent strings.
Doable by setting the `gitoxide.userAgent` variable.
1 parent f5499a5 commit 709a732

File tree

5 files changed

+36
-4
lines changed

5 files changed

+36
-4
lines changed

Diff for: git-repository/src/config/cache/access.rs

+14
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,20 @@ impl Cache {
4444
.copied()
4545
}
4646

47+
/// Returns a user agent for use with servers.
48+
pub(crate) fn user_agent_tuple(&self) -> (&'static str, Option<Cow<'static, str>>) {
49+
let agent = self
50+
.user_agent
51+
.get_or_init(|| {
52+
self.resolved
53+
.string("gitoxide", None, "userAgent")
54+
.map(|s| s.to_string())
55+
.unwrap_or_else(|| crate::env::agent().into())
56+
})
57+
.to_owned();
58+
("agent", Some(git_protocol::fetch::agent(agent).into()))
59+
}
60+
4761
pub(crate) fn personas(&self) -> &identity::Personas {
4862
self.personas
4963
.get_or_init(|| identity::Personas::from_config_and_env(&self.resolved, self.git_prefix))

Diff for: git-repository/src/config/cache/init.rs

+2
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ impl Cache {
136136
xdg_config_home_env,
137137
home_env,
138138
lenient_config,
139+
user_agent: Default::default(),
139140
personas: Default::default(),
140141
url_rewrite: Default::default(),
141142
#[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
@@ -177,6 +178,7 @@ impl Cache {
177178
self.object_kind_hint = object_kind_hint;
178179
self.reflog = reflog;
179180

181+
self.user_agent = Default::default();
180182
self.personas = Default::default();
181183
self.url_rewrite = Default::default();
182184
self.diff_algorithm = Default::default();

Diff for: git-repository/src/config/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ pub(crate) struct Cache {
124124
pub use_multi_pack_index: bool,
125125
/// The representation of `core.logallrefupdates`, or `None` if the variable wasn't set.
126126
pub reflog: Option<git_ref::store::WriteReflog>,
127+
/// The configured user agent for presentation to servers.
128+
pub(crate) user_agent: OnceCell<String>,
127129
/// identities for later use, lazy initialization.
128130
pub(crate) personas: OnceCell<identity::Personas>,
129131
/// A lazily loaded rewrite list for remote urls

Diff for: git-repository/src/remote/connection/fetch/receive_pack.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -51,22 +51,30 @@ where
5151
/// We explicitly don't special case those refs and expect the user to take control. Note that by its nature,
5252
/// force only applies to refs pointing to commits and if they don't, they will be updated either way in our
5353
/// implementation as well.
54+
///
55+
/// ### Configuration
56+
///
57+
/// - `gitoxide.userAgent` is read to obtain the application user agent for git servers and for HTTP servers as well.
5458
pub fn receive(mut self, should_interrupt: &AtomicBool) -> Result<Outcome, Error> {
5559
let mut con = self.con.take().expect("receive() can only be called once");
5660

5761
let handshake = &self.ref_map.handshake;
5862
let protocol_version = handshake.server_protocol_version;
5963

6064
let fetch = git_protocol::fetch::Command::Fetch;
61-
let fetch_features = fetch.default_features(protocol_version, &handshake.capabilities);
65+
let progress = &mut con.progress;
66+
let repo = con.remote.repo;
67+
let fetch_features = {
68+
let mut f = fetch.default_features(protocol_version, &handshake.capabilities);
69+
f.push(repo.config.user_agent_tuple());
70+
f
71+
};
6272

6373
git_protocol::fetch::Response::check_required_features(protocol_version, &fetch_features)?;
6474
let sideband_all = fetch_features.iter().any(|(n, _)| *n == "sideband-all");
6575
let mut arguments = git_protocol::fetch::Arguments::new(protocol_version, fetch_features);
6676
let mut previous_response = None::<git_protocol::fetch::Response>;
6777
let mut round = 1;
68-
let progress = &mut con.progress;
69-
let repo = con.remote.repo;
7078

7179
if self.ref_map.object_hash != repo.object_hash() {
7280
return Err(Error::IncompatibleObjectHash {

Diff for: git-repository/src/remote/connection/ref_map.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ where
6565
///
6666
/// Due to management of the transport, it's cleanest to only use it for a single interaction. Thus it's consumed along with
6767
/// the connection.
68+
///
69+
/// ### Configuration
70+
///
71+
/// - `gitoxide.userAgent` is read to obtain the application user agent for git servers and for HTTP servers as well.
6872
#[allow(clippy::result_large_err)]
6973
#[git_protocol::maybe_async::maybe_async]
7074
pub async fn ref_map(mut self, options: Options) -> Result<fetch::RefMap, Error> {
@@ -157,11 +161,13 @@ where
157161
Some(refs) => refs,
158162
None => {
159163
let specs = &self.remote.fetch_specs;
164+
let agent_feature = self.remote.repo.config.user_agent_tuple();
160165
git_protocol::fetch::refs(
161166
&mut self.transport,
162167
outcome.server_protocol_version,
163168
&outcome.capabilities,
164-
|_capabilities, arguments, _features| {
169+
move |_capabilities, arguments, features| {
170+
features.push(agent_feature);
165171
if filter_by_prefix {
166172
let mut seen = HashSet::new();
167173
for spec in specs {

0 commit comments

Comments
 (0)