Skip to content

Commit 99f4447

Browse files
committed
Fix file urls with dos driver letter on unix
I think the previous behavior was actually the correct one for Windows systems, but because my main dev machine is Linux, it is easier to make sure everything works correctly on those platforms first. I will fix Windows next.
1 parent f3e1331 commit 99f4447

File tree

2 files changed

+59
-8
lines changed

2 files changed

+59
-8
lines changed

gix-url/src/parse/mod.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,19 +163,35 @@ fn parse_scp(input: &BStr, colon: usize) -> Result<crate::Url, Error> {
163163
}
164164

165165
fn parse_file_url(input: &BStr, protocol_colon: usize) -> Result<crate::Url, Error> {
166-
let (input, url) = input_to_utf8_and_url(input, UrlKind::Url)?;
166+
let input = input_to_utf8(input, UrlKind::Url)?;
167+
let input_after_protocol = &input[protocol_colon + 3..];
167168

168-
if !input[protocol_colon + 3..].contains('/') {
169+
let Some(first_slash) = input_after_protocol.find('/') else {
169170
return Err(Error::MissingRepositoryPath {
170171
url: input.to_owned().into(),
171172
kind: UrlKind::Url,
172173
});
173-
}
174+
};
175+
176+
// We can not use the url crate to parse host and path because it special cases Windows
177+
// driver letters. With the url crate an input of `file://x:/path/to/git` is parsed as empty
178+
// host and with `x:/path/to/git` as path. This behavior is wrong for Git which parses the
179+
// `x:` as the host.
180+
// TODO: this behavior is most likely different on Windows
181+
let host = if first_slash == 0 {
182+
// file:///path/to/git
183+
None
184+
} else {
185+
// file://host/path/to/git
186+
Some(&input_after_protocol[..first_slash])
187+
};
188+
// path includes the slash character
189+
let path = &input_after_protocol[first_slash..];
174190

175191
Ok(crate::Url {
176192
serialize_alternative_form: false,
177-
host: url.host_str().map(Into::into),
178-
..parse_local(url.path().into())?
193+
host: host.map(Into::into),
194+
..parse_local(path.into())?
179195
})
180196
}
181197

gix-url/tests/parse/file.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,12 @@ fn url_from_relative_path_with_colon_in_name() -> crate::Result {
154154
Ok(())
155155
}
156156

157+
#[cfg(windows)]
157158
mod windows {
159+
use crate::parse::{assert_url, assert_url_roundtrip, url, url_alternate};
158160
use gix_url::Scheme;
159161

160162
#[test]
161-
#[cfg(windows)]
162163
fn url_from_absolute_path() -> crate::Result {
163164
assert_url(
164165
url::Url::from_directory_path("c:\\users\\1")
@@ -181,8 +182,6 @@ mod windows {
181182
Ok(())
182183
}
183184

184-
use crate::parse::{assert_url, assert_url_roundtrip, url, url_alternate};
185-
186185
#[test]
187186
fn file_path_without_protocol() -> crate::Result {
188187
let url = assert_url(
@@ -213,3 +212,39 @@ mod windows {
213212
)
214213
}
215214
}
215+
216+
#[cfg(not(windows))]
217+
mod unix {
218+
use crate::parse::{assert_url, assert_url_roundtrip, url, url_alternate};
219+
use gix_url::Scheme;
220+
221+
#[test]
222+
fn file_path_without_protocol() -> crate::Result {
223+
let url = assert_url(
224+
"x:/path/to/git",
225+
url_alternate(Scheme::Ssh, None, "x", None, b"/path/to/git"),
226+
)?
227+
.to_bstring();
228+
assert_eq!(url, "x:/path/to/git");
229+
Ok(())
230+
}
231+
232+
#[test]
233+
fn file_path_with_backslashes_without_protocol() -> crate::Result {
234+
let url = assert_url(
235+
"x:\\path\\to\\git",
236+
url_alternate(Scheme::Ssh, None, "x", None, b"\\path\\to\\git"),
237+
)?
238+
.to_bstring();
239+
assert_eq!(url, "x:\\path\\to\\git");
240+
Ok(())
241+
}
242+
243+
#[test]
244+
fn file_path_with_protocol() -> crate::Result {
245+
assert_url_roundtrip(
246+
"file://x:/path/to/git",
247+
url(Scheme::File, None, "x:", None, b"/path/to/git"),
248+
)
249+
}
250+
}

0 commit comments

Comments
 (0)