Skip to content

Commit 054edb2

Browse files
committed
refactor: Use gitoxide instead of git2
gitoxide's git_repository crate provides similar capabilities to the git2 crate. The primary advantage of gitoxide is that it is faster than git2; specifically for startup time where it is about 4x faster, and possibly for other operations. gitoxide is also written in pure Rust, which provides a richer set of interfaces and opens the door to more direct participation in upstream improvements. Also, gitoxide aims to implement more git behaviors than libgit2, and thus provides a path to eliminate calls to git which would further improve runtime performance of StGit. The potential downside to gitoxide is that it introduces a significant numbmer (40) of additional Rust crate dependencies.
1 parent 8a3a82b commit 054edb2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+3224
-1742
lines changed

Cargo.lock

Lines changed: 973 additions & 53 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ clap = { version = "4.0", default-features = false, features = [
3131
] }
3232
ctrlc = "3.2"
3333
encoding_rs = "0.8"
34-
git2 = { version = "0.15", default-features = false }
34+
git-repository = { version = "0.31", default-features = false, features = [] }
3535
indexmap = "1.8"
3636
is-terminal = "0.4"
3737
serde = { version = "1.0", features = ["derive"] }

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ dynamically links to libc and other non-Rust libraries when they are
3838
available at build-time. Dynamic link dependencies include these
3939
libraries along with their transient link dependencies:
4040

41-
- libgit2
4241
- libcurl (optional)
4342
- libbz2 (optional)
4443

src/alias.rs

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -88,35 +88,39 @@ pub(crate) fn get_default_aliases() -> Aliases {
8888
///
8989
/// The `exclude` closure is intended to prevent names of builtin StGit subcommands from
9090
/// being shadowed by aliases.
91-
pub(crate) fn get_aliases<F>(config: &git2::Config, exclude: F) -> Result<Aliases>
91+
pub(crate) fn get_aliases<F>(
92+
config_file: Option<&git_repository::config::File>,
93+
exclude: F,
94+
) -> Result<Aliases>
9295
where
9396
F: Fn(&str) -> bool,
9497
{
9598
let mut aliases = get_default_aliases();
96-
let mut iter = config.entries(None)?;
97-
while let Some(entry) = iter.next() {
98-
let entry = entry?;
99-
if let Some(name) = entry.name_bytes().strip_prefix(b"stgit.alias.") {
100-
let name = name.to_str().map_err(|_| {
101-
anyhow!(
102-
"Alias name `{}` in {} is not valid UTF-8",
103-
name.to_str_lossy(),
104-
config_level_to_str(entry.level()),
105-
)
106-
})?;
107-
if entry.has_value() {
108-
if !exclude(name) {
109-
let command = entry.value().ok_or_else(|| {
110-
anyhow!(
111-
"Alias value for `{name}` in {} is not valid UTF-8",
112-
config_level_to_str(entry.level()),
113-
)
114-
})?;
115-
let alias = Alias::new(name, command);
116-
aliases.insert(name.to_string(), alias);
99+
100+
if let Some(config_file) = config_file {
101+
if let Ok(section) = config_file.section("stgit", Some("alias".into())) {
102+
for key in section.keys() {
103+
let name = key.to_str().map_err(|_| {
104+
anyhow!(
105+
"Alias name `{}` in {} is not valid UTF-8",
106+
key.to_str_lossy(),
107+
config_source_str(section.meta().source),
108+
)
109+
})?;
110+
if let Some(value) = section.value(key) {
111+
if !exclude(name) {
112+
let command = value.to_str().map_err(|_| {
113+
anyhow!(
114+
"Alias value for `{name}` in {} is not valid UTF-8",
115+
config_source_str(section.meta().source)
116+
)
117+
})?;
118+
let alias = Alias::new(name, command);
119+
aliases.insert(name.to_string(), alias);
120+
}
121+
} else {
122+
aliases.remove(name);
117123
}
118-
} else {
119-
aliases.remove(name);
120124
}
121125
}
122126
}
@@ -172,16 +176,20 @@ fn split_command_line(line: &str) -> Result<Vec<String>, String> {
172176
}
173177
}
174178

175-
/// Map [`git2::ConfigLevel`] to user-facing strings.
176-
fn config_level_to_str(level: git2::ConfigLevel) -> &'static str {
177-
match level {
178-
git2::ConfigLevel::ProgramData => "program data config",
179-
git2::ConfigLevel::System => "system config",
180-
git2::ConfigLevel::XDG => "XDG config",
181-
git2::ConfigLevel::Global => "global config",
182-
git2::ConfigLevel::Local => "local config",
183-
git2::ConfigLevel::App => "app config",
184-
git2::ConfigLevel::Highest => "highest config",
179+
/// Map [`git_repository::config::Source`] to user-facing strings.
180+
fn config_source_str(source: git_repository::config::Source) -> &'static str {
181+
use git_repository::config::Source;
182+
match source {
183+
Source::GitInstallation => "git installed config",
184+
Source::System => "system config",
185+
Source::Git => "git config",
186+
Source::User => "user global config",
187+
Source::Local => "repository local config",
188+
Source::Worktree => "worktree config",
189+
Source::Env => "environment config",
190+
Source::Cli => "cli config",
191+
Source::Api => "api config",
192+
Source::EnvOverride => "environment override config",
185193
}
186194
}
187195

src/argset.rs

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
//! [`clap::Arg`] definitions common to several StGit commands.
44
5+
use bstr::ByteSlice;
56
use clap::Arg;
67

78
/// The `--branch`/`-b` option for selecting an alternative branch.
@@ -97,11 +98,7 @@ pub(crate) fn diff_opts_arg() -> Arg {
9798

9899
/// For use with `clap::Arg::value_parser()` to ensure a branch name is valid.
99100
pub(crate) fn parse_branch_name(name: &str) -> anyhow::Result<String> {
100-
if git2::Branch::name_is_valid(name)? {
101-
Ok(name.to_string())
102-
} else {
103-
Err(anyhow::anyhow!("invalid branch name `{name}`"))
104-
}
101+
Ok(git_repository::refs::PartialName::try_from(name).map(|_| name.to_string())?)
105102
}
106103

107104
/// Get a `&str` from a `clap::ArgMatches` instance for the given `id`.
@@ -136,15 +133,17 @@ pub(crate) fn parse_usize(s: &str) -> anyhow::Result<usize> {
136133
/// line of subordinate `git` commands.
137134
pub(crate) fn get_diff_opts(
138135
matches: &clap::ArgMatches,
139-
config: &git2::Config,
136+
config: &git_repository::config::Snapshot,
140137
force_full_index: bool,
141138
force_binary: bool,
142139
) -> Vec<String> {
143140
let mut opts = Vec::new();
144141

145-
if let Ok(value) = config.get_string("stgit.diff-opts") {
146-
for arg in value.split_ascii_whitespace() {
147-
opts.push(String::from(arg));
142+
if let Some(value) = config.string("stgit.diff-opts") {
143+
if let Ok(value) = value.to_str() {
144+
for arg in value.split_ascii_whitespace() {
145+
opts.push(String::from(arg));
146+
}
148147
}
149148
}
150149

@@ -164,14 +163,10 @@ pub(crate) fn get_diff_opts(
164163
}
165164

166165
pub(crate) fn resolve_allow_push_conflicts(
167-
config: &git2::Config,
166+
config: &git_repository::config::Snapshot,
168167
matches: &clap::ArgMatches,
169168
) -> bool {
170169
get_one_str(matches, "conflicts")
171170
.map(|s| s == "allow")
172-
.unwrap_or_else(|| {
173-
config
174-
.get_bool("stgit.push.allow-conflicts")
175-
.unwrap_or(true)
176-
})
171+
.unwrap_or_else(|| config.boolean("stgit.push.allow-conflicts").unwrap_or(true))
177172
}

0 commit comments

Comments
 (0)