-
-
Notifications
You must be signed in to change notification settings - Fork 203
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test(git): find upstream remote when using ssh (#926)
* feat: find upstream remote when using ssh The `upstream_remote` function was relying on `url::Url::parse` to extract the `owner` and `repo` from the `url`. But that only works when the repo is cloned using a URL, e.g. `https://github.com/orhun/git-cliff.git`. However, this would fail to parse when cloned using SSH, e.g. `[email protected]:orhun/git-cliff.git`. If the url::URL::parser fails, we now try to parse an SSH remote in the format `git@hostname:owner/repo.git`. The error from `upstream_remote` also notes that a posible reason for it failing would be that the `HEAD` is detached. * Update git-cliff-core/src/repo.rs * Update git-cliff-core/src/repo.rs --------- Co-authored-by: Orhun Parmaksız <[email protected]>
- Loading branch information
Showing
2 changed files
with
90 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,8 @@ Note that we have a [Code of Conduct](./CODE_OF_CONDUCT.md), please follow it in | |
|
||
```sh | ||
git clone https://github.com/{username}/git-cliff && cd git-cliff | ||
# OR | ||
git clone [email protected]:{username}/git-cliff && cd git-cliff | ||
``` | ||
|
||
To ensure the successful execution of the tests, it is essential to fetch the tags as follows: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -399,6 +399,8 @@ impl Repository { | |
/// | ||
/// Find the branch that HEAD points to, and read the remote configured for | ||
/// that branch returns the remote and the name of the local branch. | ||
/// | ||
/// Note: HEAD must not be detached. | ||
pub fn upstream_remote(&self) -> Result<Remote> { | ||
for branch in self.inner.branches(Some(BranchType::Local))? { | ||
let branch = branch?.0; | ||
|
@@ -424,30 +426,79 @@ impl Repository { | |
})? | ||
.to_string(); | ||
trace!("Upstream URL: {url}"); | ||
let url = Url::parse(&url)?; | ||
let segments: Vec<&str> = url | ||
.path_segments() | ||
.ok_or_else(|| { | ||
Error::RepoError(String::from("failed to get URL segments")) | ||
})? | ||
.rev() | ||
.collect(); | ||
if let (Some(owner), Some(repo)) = | ||
(segments.get(1), segments.first()) | ||
{ | ||
return Ok(Remote { | ||
owner: owner.to_string(), | ||
repo: repo.trim_end_matches(".git").to_string(), | ||
token: None, | ||
is_custom: false, | ||
}); | ||
} | ||
return find_remote(&url); | ||
} | ||
} | ||
Err(Error::RepoError(String::from("no remotes configured"))) | ||
Err(Error::RepoError(String::from( | ||
"no remotes configured or HEAD is detached", | ||
))) | ||
} | ||
} | ||
|
||
fn find_remote(url: &str) -> Result<Remote> { | ||
url_path_segments(url).or_else(|err| { | ||
if url.contains("@") && url.contains(":") && url.contains("/") { | ||
ssh_path_segments(url) | ||
} else { | ||
Err(err) | ||
} | ||
}) | ||
} | ||
|
||
/// Returns the Remote from parsing the HTTPS format URL. | ||
/// | ||
/// This function expects the URL to be in the following format: | ||
/// | ||
/// > https://hostname/query/path.git | ||
fn url_path_segments(url: &str) -> Result<Remote> { | ||
let parsed_url = Url::parse(url.strip_suffix(".git").unwrap_or(url))?; | ||
let segments: Vec<&str> = parsed_url | ||
.path_segments() | ||
.ok_or_else(|| Error::RepoError(String::from("failed to get URL segments")))? | ||
.rev() | ||
.collect(); | ||
let [repo, owner, ..] = &segments[..] else { | ||
return Err(Error::RepoError(String::from( | ||
"failed to get the owner and repo", | ||
))); | ||
}; | ||
Ok(Remote { | ||
owner: owner.to_string(), | ||
repo: repo.to_string(), | ||
token: None, | ||
is_custom: false, | ||
}) | ||
} | ||
|
||
/// Returns the Remote from parsing the SSH format URL. | ||
/// | ||
/// This function expects the URL to be in the following format: | ||
/// | ||
/// > git@hostname:owner/repo.git | ||
fn ssh_path_segments(url: &str) -> Result<Remote> { | ||
let [_, owner_repo, ..] = url | ||
.strip_suffix(".git") | ||
.unwrap_or(url) | ||
.split(":") | ||
.collect::<Vec<_>>()[..] | ||
else { | ||
return Err(Error::RepoError(String::from( | ||
"failed to get the owner and repo from ssh remote (:)", | ||
))); | ||
}; | ||
let [owner, repo] = owner_repo.split("/").collect::<Vec<_>>()[..] else { | ||
return Err(Error::RepoError(String::from( | ||
"failed to get the owner and repo from ssh remote (/)", | ||
))); | ||
}; | ||
Ok(Remote { | ||
owner: owner.to_string(), | ||
repo: repo.to_string(), | ||
token: None, | ||
is_custom: false, | ||
}) | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::*; | ||
|
@@ -502,6 +553,24 @@ mod test { | |
) | ||
} | ||
|
||
#[test] | ||
fn http_url_repo_owner() -> Result<()> { | ||
let url = "https://hostname.com/bob/magic.git"; | ||
let remote = find_remote(url)?; | ||
assert_eq!(remote.owner, "bob", "match owner"); | ||
assert_eq!(remote.repo, "magic", "match repo"); | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn ssh_url_repo_owner() -> Result<()> { | ||
let url = "[email protected]:bob/magic.git"; | ||
let remote = find_remote(url)?; | ||
assert_eq!(remote.owner, "bob", "match owner"); | ||
assert_eq!(remote.repo, "magic", "match repo"); | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn get_latest_commit() -> Result<()> { | ||
let repository = get_repository()?; | ||
|