create git branch name module

This commit is contained in:
Nikita Galaiko 2023-07-18 15:49:13 +02:00
parent 4299d63074
commit 11730aa7af
6 changed files with 141 additions and 31 deletions

6
butler/Cargo.lock generated
View File

@ -1655,13 +1655,8 @@ name = "git-butler-tauri"
version = "0.0.0"
dependencies = [
"anyhow",
"async-trait",
"bytes",
"clap",
"colored 2.0.4",
"dialoguer",
"diffy",
"dirs 5.0.1",
"filetime",
"fslock",
"futures",
@ -1696,6 +1691,7 @@ dependencies = [
"tokio",
"tokio-tungstenite 0.18.0",
"tokio-util",
"url",
"urlencoding",
"uuid",
"walkdir",

View File

@ -6,7 +6,7 @@ use tokio_util::sync::CancellationToken;
use crate::{
bookmarks, database, deltas, events, files, gb_repository,
project_repository::{self, activity, conflicts, diff},
project_repository::{self, activity, conflicts, diff, branch},
projects, pty, search, sessions, storage, users, virtual_branches, watcher,
};
@ -577,14 +577,14 @@ impl App {
project_repository.git_match_paths(pattern)
}
pub fn git_branches(&self, project_id: &str) -> Result<Vec<String>> {
pub fn git_branches(&self, project_id: &str) -> Result<Vec<branch::LocalName>> {
let project = self.gb_project(project_id)?;
let project_repository = project_repository::Repository::open(&project)
.context("failed to open project repository")?;
project_repository.git_branches()
}
pub fn git_remote_branches(&self, project_id: &str) -> Result<Vec<String>> {
pub fn git_remote_branches(&self, project_id: &str) -> Result<Vec<branch::RemoteName>> {
let project = self.gb_project(project_id)?;
let project_repository = project_repository::Repository::open(&project)
.context("failed to open project repository")?;

View File

@ -38,7 +38,7 @@ use tauri_plugin_log::{
use thiserror::Error;
use timed::timed;
use crate::project_repository::activity;
use crate::project_repository::{activity, branch};
#[derive(Debug, Error)]
pub enum Error {
@ -386,7 +386,7 @@ async fn git_match_paths(
#[timed(duration(printer = "debug!"))]
#[tauri::command(async)]
async fn git_branches(handle: tauri::AppHandle, project_id: &str) -> Result<Vec<String>, Error> {
async fn git_branches(handle: tauri::AppHandle, project_id: &str) -> Result<Vec<branch::LocalName>, Error> {
let app = handle.state::<app::App>();
let branches = app
.git_branches(project_id)
@ -399,7 +399,7 @@ async fn git_branches(handle: tauri::AppHandle, project_id: &str) -> Result<Vec<
async fn git_remote_branches(
handle: tauri::AppHandle,
project_id: &str,
) -> Result<Vec<String>, Error> {
) -> Result<Vec<branch::RemoteName>, Error> {
let app = handle.state::<app::App>();
let branches = app.git_remote_branches(project_id).with_context(|| {
format!(

View File

@ -0,0 +1,113 @@
use std::fmt;
use serde::Serialize;
#[derive(Debug)]
pub struct RemoteName {
// contains name of the remote, e.x. "origin" or "upstream"
remote: String,
// contains name of the branch, e.x. "master" or "main"
branch: String,
}
impl fmt::Display for RemoteName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}/{}", self.remote, self.branch)
}
}
impl Serialize for RemoteName {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.to_string())
}
}
impl TryFrom<&git2::Branch<'_>> for RemoteName {
type Error = git2::Error;
fn try_from(value: &git2::Branch<'_>) -> std::result::Result<Self, Self::Error> {
if !value.get().is_remote() {
return Err(git2::Error::from_str("not a remote branch"));
}
let name = String::from_utf8(value.name_bytes()?.to_vec())
.map_err(|e| git2::Error::from_str(&e.to_string()))?;
if let Some((remote, branch)) = name.split_once('/') {
Ok(Self {
remote: remote.to_string(),
branch: branch.to_string(),
})
} else {
Err(git2::Error::from_str("invalid remote branch name"))
}
}
}
#[derive(Debug)]
pub struct LocalName {
// contains name of the branch, e.x. "master" or "main"
branch: String,
// contains name of the remote branch, if the local branch is tracking a remote branch
remote: Option<RemoteName>,
}
impl Serialize for LocalName {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.branch)
}
}
impl fmt::Display for LocalName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.branch)
}
}
impl TryFrom<&git2::Branch<'_>> for LocalName {
type Error = git2::Error;
fn try_from(value: &git2::Branch<'_>) -> std::result::Result<Self, Self::Error> {
if value.get().is_remote() {
return Err(git2::Error::from_str("not a local branch"));
}
let name = String::from_utf8(value.name_bytes()?.to_vec())
.map_err(|e| git2::Error::from_str(&e.to_string()))?;
match value.upstream() {
Ok(upstream) => {
let remote_branch_name = RemoteName::try_from(&upstream)?;
Ok(Self {
branch: name,
remote: Some(remote_branch_name),
})
}
Err(error) => {
if error.code() == git2::ErrorCode::NotFound {
Ok(Self {
branch: name,
remote: None,
})
} else {
Err(error)
}
}
}
}
}
#[derive(Debug)]
pub enum Name {
Remote(RemoteName),
Local(LocalName),
}
impl TryFrom<&git2::Branch<'_>> for Name {
type Error = git2::Error;
fn try_from(value: &git2::Branch<'_>) -> std::result::Result<Self, Self::Error> {
if value.get().is_remote() {
Ok(Self::Remote(RemoteName::try_from(value)?))
} else {
Ok(Self::Local(LocalName::try_from(value)?))
}
}
}

View File

@ -1,6 +1,7 @@
pub mod activity;
pub mod conflicts;
pub mod diff;
pub mod branch;
mod repository;
pub use repository::{Error, FileStatus, Repository};

View File

@ -8,6 +8,8 @@ use walkdir::WalkDir;
use crate::{project_repository::activity, projects, reader};
use super::branch;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
@ -218,30 +220,28 @@ impl<'repository> Repository<'repository> {
Ok(files)
}
pub fn git_branches(&self) -> Result<Vec<String>> {
let mut branches = vec![];
for branch in self
.git_repository
pub fn git_branches(&self) -> Result<Vec<branch::LocalName>> {
self.git_repository
.branches(Some(git2::BranchType::Local))?
{
let (branch, _) = branch?;
let name = branch.name()?.unwrap().to_string();
branches.push(name);
}
Ok(branches)
.flatten()
.map(|(branch, _)| branch)
.map(|branch| {
branch::LocalName::try_from(&branch)
.context("failed to convert branch to local name")
})
.collect::<Result<Vec<branch::LocalName>>>()
}
pub fn git_remote_branches(&self) -> Result<Vec<String>> {
let mut branches = vec![];
for branch in self
.git_repository
pub fn git_remote_branches(&self) -> Result<Vec<branch::RemoteName>> {
self.git_repository
.branches(Some(git2::BranchType::Remote))?
{
let (branch, _) = branch?;
let name = branch.name()?.unwrap().to_string();
branches.push(name);
}
Ok(branches)
.flatten()
.map(|(branch, _)| branch)
.map(|branch| {
branch::RemoteName::try_from(&branch)
.context("failed to convert branch to remote name")
})
.collect::<Result<Vec<branch::RemoteName>>>()
}
// returns a list of commit oids from the first oid to the second oid