Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Delta has many features and is very customizable; please see `delta -h` (short h
- Line numbering
- `n` and `N` keybindings to move between files in large diffs, and between diffs in `log -p` views (`--navigate`)
- Improved merge conflict display
- Improved `git blame` display (syntax highlighting; `--hyperlinks` formats commits as links to hosting provider etc. Supported hosting providers are: GitHub, GitLab, SourceHut, Codeberg)
- Improved `git blame` display (syntax highlighting; `--hyperlinks` formats commits as links to hosting provider etc. Supported hosting providers are: GitHub, GitLab, SourceHut, Codeberg, Bitbucket)
- Syntax-highlights grep output from `rg`, `git grep`, `grep`, etc
- Support for Git's `--color-moved` feature.
- Code can be copied directly from the diff (`-/+` markers are removed by default).
Expand Down
64 changes: 63 additions & 1 deletion src/git_config/remote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub enum GitRemoteRepo {
GitLab { slug: String },
SourceHut { slug: String },
Codeberg { slug: String },
Bitbucket { slug: String },
}

impl GitRemoteRepo {
Expand All @@ -29,6 +30,9 @@ impl GitRemoteRepo {
Self::Codeberg { slug } => {
format!("https://codeberg.org/{slug}/commit/{commit}")
}
Self::Bitbucket { slug } => {
format!("https://bitbucket.org/{slug}/commits/{commit}")
}
}
}

Expand Down Expand Up @@ -97,6 +101,20 @@ lazy_static! {
"
)
.unwrap();
static ref BITBUCKET_REMOTE_URL: Regex = Regex::new(
r"(?x)
^
(?:https://|git@)? # Support both HTTPS and SSH URLs, SSH URLs optionally omitting the git@
bitbucket\.org
[:/] # This separator differs between SSH and HTTPS URLs
([^/]+) # Capture the user/org name
/
(.+?) # Capture the repo name (lazy to avoid consuming '.git' if present)
(?:\.git)? # Non-capturing group to consume '.git' if present
$
"
)
.unwrap();
}

impl FromStr for GitRemoteRepo {
Expand Down Expand Up @@ -135,8 +153,18 @@ impl FromStr for GitRemoteRepo {
repo = caps.get(2).unwrap().as_str()
),
})
} else if let Some(caps) = BITBUCKET_REMOTE_URL.captures(s) {
Ok(Self::Bitbucket {
slug: format!(
"{user}/{repo}",
user = caps.get(1).unwrap().as_str(),
repo = caps.get(2).unwrap().as_str()
),
})
} else {
Err(anyhow!("Not a GitHub, GitLab, SourceHut or Codeberg repo."))
Err(anyhow!(
"Not a GitHub, GitLab, SourceHut, Codeberg or Bitbucket repo."
))
}
}
}
Expand Down Expand Up @@ -288,4 +316,38 @@ mod tests {
format!("https://codeberg.org/dnkl/foot/commit/{commit_hash}")
)
}

#[test]
fn test_parse_bitbucket_urls() {
let urls = &[
"https://bitbucket.org/someuser/somerepo.git",
"https://bitbucket.org/someuser/somerepo",
"git@bitbucket.org:someuser/somerepo.git",
"git@bitbucket.org:someuser/somerepo",
"bitbucket.org:someuser/somerepo.git",
"bitbucket.org:someuser/somerepo",
];
for url in urls {
let parsed = GitRemoteRepo::from_str(url);
assert!(parsed.is_ok());
assert_eq!(
parsed.unwrap(),
GitRemoteRepo::Bitbucket {
slug: "someuser/somerepo".to_string()
}
);
}
}

#[test]
fn test_format_bitbucket_commit_link() {
let repo = GitRemoteRepo::Bitbucket {
slug: "someuser/somerepo".to_string(),
};
let commit_hash = "1c072856ebf12419378c5098ad543c497197c6da";
assert_eq!(
repo.format_commit_url(commit_hash),
format!("https://bitbucket.org/someuser/somerepo/commits/{commit_hash}")
)
}
}