mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-18 23:02:31 +03:00
Merge pull request #2287 from gitbutlerapp/core-git
add core gitbutler-git crate
This commit is contained in:
commit
62c3322154
1
.github/workflows/push.yaml
vendored
1
.github/workflows/push.yaml
vendored
@ -77,3 +77,4 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: ./.github/actions/init-env-rust
|
- uses: ./.github/actions/init-env-rust
|
||||||
- run: cargo clippy --all-targets --all-features --tests
|
- run: cargo clippy --all-targets --all-features --tests
|
||||||
|
- run: cargo clippy --no-default-features --tests -p gitbutler-git
|
||||||
|
9
Cargo.lock
generated
9
Cargo.lock
generated
@ -1871,6 +1871,15 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gitbutler-git"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"git2",
|
||||||
|
"serde",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glib"
|
name = "glib"
|
||||||
version = "0.15.12"
|
version = "0.15.12"
|
||||||
|
@ -3,6 +3,7 @@ members = [
|
|||||||
"gitbutler-app",
|
"gitbutler-app",
|
||||||
"gitbutler-core",
|
"gitbutler-core",
|
||||||
"gitbutler-diff",
|
"gitbutler-diff",
|
||||||
|
"gitbutler-git",
|
||||||
]
|
]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
|
15
gitbutler-git/Cargo.toml
Normal file
15
gitbutler-git/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "gitbutler-git"
|
||||||
|
version = "0.0.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["git2", "serde"]
|
||||||
|
git2 = ["dep:git2", "std"]
|
||||||
|
serde = ["dep:serde"]
|
||||||
|
std = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
git2 = { workspace = true, optional = true }
|
||||||
|
thiserror.workspace = true
|
||||||
|
serde = { workspace = true, optional = true }
|
2
gitbutler-git/src/backend.rs
Normal file
2
gitbutler-git/src/backend.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#[cfg(feature = "git2")]
|
||||||
|
pub mod git2;
|
8
gitbutler-git/src/backend/git2.rs
Normal file
8
gitbutler-git/src/backend/git2.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
//! [libgit2](https://libgit2.org/) implementation of
|
||||||
|
//! the core `gitbutler-git` library traits.
|
||||||
|
//!
|
||||||
|
//! The entry point for this module is the [`Repository`] struct.
|
||||||
|
|
||||||
|
mod repository;
|
||||||
|
|
||||||
|
pub use self::repository::Repository;
|
71
gitbutler-git/src/backend/git2/repository.rs
Normal file
71
gitbutler-git/src/backend/git2/repository.rs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::ConfigScope;
|
||||||
|
|
||||||
|
/// A [`crate::Repository`] implementation using the `git2` crate.
|
||||||
|
pub struct Repository {
|
||||||
|
repo: git2::Repository,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Repository {
|
||||||
|
/// Opens a repository at the given path.
|
||||||
|
#[inline]
|
||||||
|
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, git2::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
repo: git2::Repository::open(path)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::Repository for Repository {
|
||||||
|
type Error = git2::Error;
|
||||||
|
|
||||||
|
async fn config_get(
|
||||||
|
&self,
|
||||||
|
key: &str,
|
||||||
|
scope: ConfigScope,
|
||||||
|
) -> Result<Option<String>, Self::Error> {
|
||||||
|
let config = self.repo.config()?;
|
||||||
|
|
||||||
|
let res = match scope {
|
||||||
|
ConfigScope::Auto => config.get_string(key),
|
||||||
|
ConfigScope::Local => config.open_level(git2::ConfigLevel::Local)?.get_string(key),
|
||||||
|
ConfigScope::System => config
|
||||||
|
.open_level(git2::ConfigLevel::System)?
|
||||||
|
.get_string(key),
|
||||||
|
ConfigScope::Global => config
|
||||||
|
.open_level(git2::ConfigLevel::Global)?
|
||||||
|
.get_string(key),
|
||||||
|
};
|
||||||
|
|
||||||
|
res.map(Some).or_else(|e| {
|
||||||
|
if e.code() == git2::ErrorCode::NotFound {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn config_set(
|
||||||
|
&self,
|
||||||
|
key: &str,
|
||||||
|
value: &str,
|
||||||
|
scope: ConfigScope,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
let mut config = self.repo.config()?;
|
||||||
|
|
||||||
|
match scope {
|
||||||
|
ConfigScope::Auto => config.set_str(key, value),
|
||||||
|
ConfigScope::Local => config
|
||||||
|
.open_level(git2::ConfigLevel::Local)?
|
||||||
|
.set_str(key, value),
|
||||||
|
ConfigScope::System => config
|
||||||
|
.open_level(git2::ConfigLevel::System)?
|
||||||
|
.set_str(key, value),
|
||||||
|
ConfigScope::Global => config
|
||||||
|
.open_level(git2::ConfigLevel::Global)?
|
||||||
|
.set_str(key, value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
gitbutler-git/src/lib.rs
Normal file
24
gitbutler-git/src/lib.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//! GitButler core library for interacting with Git.
|
||||||
|
//!
|
||||||
|
//! This library houses a number of Git implementations,
|
||||||
|
//! over which we abstract a common interface and provide
|
||||||
|
//! higher-level operations that are implementation-agnostic.
|
||||||
|
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)] // must be first
|
||||||
|
#![feature(error_in_core)]
|
||||||
|
#![deny(missing_docs, unsafe_code)]
|
||||||
|
#![allow(async_fn_in_trait)]
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
mod backend;
|
||||||
|
pub mod ops;
|
||||||
|
mod repository;
|
||||||
|
|
||||||
|
pub(crate) mod prelude;
|
||||||
|
|
||||||
|
#[cfg(feature = "git2")]
|
||||||
|
pub use backend::git2;
|
||||||
|
|
||||||
|
pub use self::repository::{ConfigScope, Repository};
|
30
gitbutler-git/src/ops.rs
Normal file
30
gitbutler-git/src/ops.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//! High-level operations that are implementation-agnostic.
|
||||||
|
//!
|
||||||
|
//! These operations are similar to Git's non-plumbing commands,
|
||||||
|
//! in that they compose both high- and low-level operations
|
||||||
|
//! into more complex operations, without caring about the
|
||||||
|
//! underlying implementation.
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
use crate::{ConfigScope, Repository};
|
||||||
|
|
||||||
|
/// Returns whether or not the repository has GitButler's
|
||||||
|
/// utmost discretion enabled.
|
||||||
|
pub async fn has_utmost_discretion<R: Repository>(repo: &R) -> Result<bool, R::Error> {
|
||||||
|
let config = repo
|
||||||
|
.config_get("gitbutler.utmostDiscretion", ConfigScope::Auto)
|
||||||
|
.await?;
|
||||||
|
Ok(config == Some("true".to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets whether or not the repository has GitButler's utmost discretion.
|
||||||
|
pub async fn set_utmost_discretion<R: Repository>(repo: &R, value: bool) -> Result<(), R::Error> {
|
||||||
|
repo.config_set(
|
||||||
|
"gitbutler.utmostDiscretion",
|
||||||
|
if value { "true" } else { "false" },
|
||||||
|
ConfigScope::Local,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
2
gitbutler-git/src/prelude.rs
Normal file
2
gitbutler-git/src/prelude.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
pub use alloc::string::{String, ToString};
|
44
gitbutler-git/src/repository.rs
Normal file
44
gitbutler-git/src/repository.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#[allow(unused_imports)]
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
/// The scope from/to which a configuration value is read/written.
|
||||||
|
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
pub enum ConfigScope {
|
||||||
|
/// Pull from the most appropriate scope.
|
||||||
|
/// This is the default, and will fall back to a higher
|
||||||
|
/// scope if the value is not initially found.
|
||||||
|
#[default]
|
||||||
|
Auto = 0,
|
||||||
|
/// Pull from the local scope (`.git/config`) _only_.
|
||||||
|
Local = 1,
|
||||||
|
/// Pull from the system-wide scope (`${prefix}/etc/gitconfig`) _only_.
|
||||||
|
System = 2,
|
||||||
|
/// Pull from the global (user) scope (typically `~/.gitconfig`) _only_.
|
||||||
|
Global = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A handle to an open Git repository.
|
||||||
|
pub trait Repository {
|
||||||
|
/// The type of error returned by this repository.
|
||||||
|
type Error: core::error::Error + Send + Sync + 'static;
|
||||||
|
|
||||||
|
/// Reads a configuration value.
|
||||||
|
///
|
||||||
|
/// Errors if the value is not valid UTF-8.
|
||||||
|
async fn config_get(
|
||||||
|
&self,
|
||||||
|
key: &str,
|
||||||
|
scope: ConfigScope,
|
||||||
|
) -> Result<Option<String>, Self::Error>;
|
||||||
|
|
||||||
|
/// Writes a configuration value.
|
||||||
|
///
|
||||||
|
/// Errors if the new value is not valid UTF-8.
|
||||||
|
async fn config_set(
|
||||||
|
&self,
|
||||||
|
key: &str,
|
||||||
|
value: &str,
|
||||||
|
scope: ConfigScope,
|
||||||
|
) -> Result<(), Self::Error>;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user