Skip to content

montly report #1854

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 22, 2025
Merged
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ bin-dir = "gitoxide-max-pure-v{ version }-{ target }/{ bin }{ binary-ext }"
pedantic = { level = "warn", priority = -1 }
#
# Reviewed and allowed lints
needless_continue = "allow"
enum_glob_use = "allow" # x97
missing_errors_doc = "allow" # x1792
missing_panics_doc = "allow" # x447
Expand Down
2 changes: 1 addition & 1 deletion deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# More documentation for the advisories section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
[advisories]
ignore = []
ignore = ['RUSTSEC-2025-0007']



Expand Down
32 changes: 32 additions & 0 deletions etc/reports/25-02.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Like the previous month was the first in 2025 with plenty of news to share, this month is a first as well: I seem to have nothing to write about.

Sure, there were a couple of smaller API improvements, but nothing that would inspire me.
So what did I do all this time? Probably this is the moment where there really is no other way but to talk about GitButler, something I avoided last month just because it doesn't seem to belong into the `gitoxide` newsletter.

## GitButler

[GitButler](https://gitbutler.com) is what I have been working on intensely for the last two months effectively, in a push to help it to unfold its true potential. Thus far it was mostly powered by `git2`, with `gitoxide` sprinkled in there, but the next iteration will be the inverse with `git2` only being used where `gitoxide` is lacking a feature. That way, along with massive architectural changes, it will be able to cope with large repositories and be more compatible with various Git features, too.

I cannot wait to see all this work to finally come to fruition, and of course, to also see myself being pulled to a user interface that truly elevates my workflow and the workflow of other devs just like me who thus far preferred to stay on the command-line.

## Community

### Faster `gix blame`

Christoph Rüßler kept working and managed to greatly improve the `gix blame` performance while increasing its conformance to Git at the same time. This means that now, depending on the sample, `gix blame` *can* be a bit faster than Git, but it typically is still up to 30% slower when commitgraph caches are used. Overall though, the performance is nothing to sneeze at, and it competes quite well except for in pathological cases.
Admittedly, I am quite blown away by the performance and have a feeling that last time I checked, I didn't use the latest version of the `gix` binary.
It's worth noting that rename-tracking still isn't support, but I also see no reason why it shouldn't be eventually, a feature that would make `gix blame` so much more useful in practice.

### `gix blame` with experimental cache

A pretty [slim PR](https://github.com/GitoxideLabs/gitoxide/pull/1852) shows how to use a special cache to greatly speedup blames, from ~300ms down to just ~4ms, a massive 75x speedup that would be very useful for editors and IDEs, or forges, I am sure!
Of course, one first has to build such cache, and probably do so per file, but I am sure there are plenty of use-cases for it when it's driven by dev tooling.

### Gix in Cargo

With `gix status` now available I am planning to integrate it as soon as possible! That didn't happen yet, but… it will, the stack to work off before I can start this is pretty high though so it's unlikely to happen anytime soon.

Cheers
Sebastian

PS: The latest timesheets can be found [here (2025)](https://github.com/Byron/byron/blob/main/timesheets/2025.csv).
2 changes: 1 addition & 1 deletion gitoxide-core/src/hours/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ where
break;
}
Err(err) => return Err(err.into()),
};
}
}
if let Some(tx) = tx_tree_id {
tx.send(chunk).ok();
Expand Down
8 changes: 4 additions & 4 deletions gitoxide-core/src/index/information.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,16 @@ mod serde_only {
});
if f.link().is_some() {
names.push("link");
};
}
if f.resolve_undo().is_some() {
names.push("resolve-undo (REUC)");
};
}
if f.untracked().is_some() {
names.push("untracked (UNTR)");
};
}
if f.fs_monitor().is_some() {
names.push("fs-monitor (FSMN)");
};
}
if f.had_offset_table() {
names.push("offset-table (IEOT)");
}
Expand Down
2 changes: 1 addition & 1 deletion gitoxide-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ pub use discover::discover;
pub fn env(mut out: impl std::io::Write, format: OutputFormat) -> anyhow::Result<()> {
if format != OutputFormat::Human {
bail!("JSON output isn't supported");
};
}

let width = 15;
writeln!(
Expand Down
2 changes: 1 addition & 1 deletion gitoxide-core/src/pack/explode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ pub fn pack_or_pack_index(
)
})?;

if !object_path.as_ref().map_or(true, |p| p.as_ref().is_dir()) {
if !object_path.as_ref().is_none_or(|p| p.as_ref().is_dir()) {
return Err(anyhow!(
"The object directory at '{}' is inaccessible",
object_path
Expand Down
2 changes: 1 addition & 1 deletion gitoxide-core/src/pack/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ pub fn from_pack(
OutputFormat::Human => drop(human_output(out, res)),
#[cfg(feature = "serde")]
OutputFormat::Json => serde_json::to_writer_pretty(out, &res)?,
};
}
Ok(())
}

Expand Down
2 changes: 1 addition & 1 deletion gitoxide-core/src/pack/receive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,6 @@ fn receive_pack_blocking(
OutputFormat::Json => {
serde_json::to_writer_pretty(&mut out, &JsonOutcome::from_outcome_and_refs(outcome, refs))?;
}
};
}
Ok(())
}
4 changes: 2 additions & 2 deletions gitoxide-core/src/pack/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ where
#[cfg(feature = "serde")]
Some(OutputFormat::Json) => serde_json::to_writer_pretty(out, &multi_index.index_names().iter().zip(res.pack_traverse_statistics).collect::<Vec<_>>())?,
_ => {}
};
}
return Ok(())
},
_ => return Err(anyhow!(
Expand All @@ -195,7 +195,7 @@ where
#[cfg(feature = "serde")]
Some(OutputFormat::Json) => serde_json::to_writer_pretty(out, stats)?,
_ => {}
};
}
}
Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion gitoxide-core/src/query/engine/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ pub fn update(
break;
}
Err(err) => return Err(err.into()),
};
}
}
db.send_last_chunk();
let saw_new_commits = !commits.is_empty();
Expand Down
2 changes: 1 addition & 1 deletion gitoxide-core/src/repository/clean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ pub(crate) mod function {
continue;
}
}
};
}

let is_ignored = matches!(entry.status, gix::dir::entry::Status::Ignored(_));
let entry_path = gix::path::from_bstr(entry.rela_path);
Expand Down
2 changes: 1 addition & 1 deletion gitoxide-core/src/repository/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ pub(crate) mod function {
&mut err,
)?;
}
};
}

if let Some(gix::worktree::state::checkout::Outcome { collisions, errors, .. }) = outcome {
if !(collisions.is_empty() && errors.is_empty()) {
Expand Down
2 changes: 1 addition & 1 deletion gitoxide-core/src/repository/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ impl Filter {
}
(None, _) => {}
(Some(_), None) => return false,
};
}
true
}
}
Expand Down
2 changes: 1 addition & 1 deletion gitoxide-core/src/repository/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ fn write_changes(
writeln!(out, " {:o} -> {:o}", source_entry_mode.0, entry_mode.0)?;
}
}
};
}
}

Ok(())
Expand Down
2 changes: 1 addition & 1 deletion gitoxide-core/src/repository/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ pub(crate) mod function {
gix::remote::fetch::refmap::Source::Ref(r) => {
crate::repository::remote::refs::print_ref(&mut out, r)?;
}
};
}
let mode_and_type = update.type_change.map_or_else(
|| format!("{}", update.mode),
|type_change| {
Expand Down
2 changes: 1 addition & 1 deletion gitoxide-core/src/repository/index/entries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ pub(crate) mod function {
.unwrap_or_default()
},
)
.map_or(true, |m| m.is_excluded());
.is_none_or(|m| m.is_excluded());

let entry_is_submodule = entry.mode.is_submodule();
if entry_is_excluded && (!entry_is_submodule || !recurse_submodules) {
Expand Down
2 changes: 1 addition & 1 deletion gitoxide-core/src/repository/remote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ mod refs_impl {
out,
&map.remote_refs.into_iter().map(JsonRef::from).collect::<Vec<_>>(),
)?,
};
}
Ok(())
}
}
Expand Down
2 changes: 1 addition & 1 deletion gitoxide-core/src/repository/submodule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fn print_sm(sm: Submodule<'_>, dirty_suffix: Option<&str>, out: &mut impl std::i
let mut sm_repo = sm.open()?;
if let Some(repo) = sm_repo.as_mut() {
repo.object_cache_size_if_unset(4 * 1024 * 1024);
};
}
writeln!(
out,
" {is_active} {path} {config} head:{head_id} index:{index_id} ({worktree}) [{url}]",
Expand Down
7 changes: 1 addition & 6 deletions gix-attributes/src/search/outcome.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,12 +237,7 @@ impl Outcome {
}

fn reduce_and_check_if_done(&mut self, attr: AttributeId) -> bool {
if self.selected.is_empty()
|| self
.selected
.iter()
.any(|(_name, id)| id.map_or(false, |id| id == attr))
{
if self.selected.is_empty() || self.selected.iter().any(|(_name, id)| *id == Some(attr)) {
*self.remaining.as_mut().expect("initialized") -= 1;
}
self.is_done()
Expand Down
2 changes: 1 addition & 1 deletion gix-blame/src/file/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ fn collect_parents(
parent_ids.push((id, parent_commit_time));
}
}
};
}
Ok(parent_ids)
}

Expand Down
2 changes: 1 addition & 1 deletion gix-commitgraph/src/file/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ impl File {
x => {
return Err(Error::UnsupportedVersion(x));
}
};
}
ofs += 1;

let object_hash = gix_hash::Kind::try_from(data[ofs]).map_err(Error::UnsupportedHashVersion)?;
Expand Down
4 changes: 2 additions & 2 deletions gix-config-value/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ impl<'a> Path<'a> {
/// the `home_for_user` function to be provided.
/// The interpolation uses `getpwnam` sys call and is therefore not available on windows.
/// - `%(prefix)/` is expanded to the location where `gitoxide` is installed.
/// This location is not known at compile time and therefore need to be
/// optionally provided by the caller through `git_install_dir`.
/// This location is not known at compile time and therefore need to be
/// optionally provided by the caller through `git_install_dir`.
///
/// Any other, non-empty path value is returned unchanged and error is returned in case of an empty path value or if required input
/// wasn't provided.
Expand Down
2 changes: 1 addition & 1 deletion gix-config/src/file/includes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ impl File<'static> {
/// which later overwrite portions of the included file, which seems unusual as these would be related to `includes`.
/// We can fix this by 'splitting' the include section if needed so the included sections are put into the right place.
/// - `hasconfig:remote.*.url` will not prevent itself to include files with `[remote "name"]\nurl = x` values, but it also
/// won't match them, i.e. one cannot include something that will cause the condition to match or to always be true.
/// won't match them, i.e. one cannot include something that will cause the condition to match or to always be true.
pub fn resolve_includes(&mut self, options: init::Options<'_>) -> Result<(), Error> {
if options.includes.max_depth == 0 {
return Ok(());
Expand Down
2 changes: 1 addition & 1 deletion gix-config/src/file/init/from_paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ impl File<'static> {
return Err(Error::Io { source: err, path });
}
}
};
}
meta.path = Some(path);

let config = Self::from_bytes_owned(buf, meta, options)?;
Expand Down
2 changes: 1 addition & 1 deletion gix-config/src/file/section/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ impl Body<'_> {
value_range.end = i;
} else {
value_range.start = i;
};
}
}
_ => (),
}
Expand Down
2 changes: 1 addition & 1 deletion gix-config/src/parse/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ fn from_bytes<'a, 'b>(
events: std::mem::take(&mut events),
});
}
};
}
header = match convert(Event::SectionHeader(next_header)) {
Event::SectionHeader(h) => h,
_ => unreachable!("BUG: convert must not change the event type, just the lifetime"),
Expand Down
2 changes: 1 addition & 1 deletion gix-credentials/tests/program/from_custom_definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const SH: &str = "/bin/sh";
fn empty() {
let prog = Program::from_custom_definition("");
let git = *GIT;
assert!(matches!(&prog.kind, Kind::ExternalName { name_and_args } if name_and_args == ""));
assert!(matches!(&prog.kind, Kind::ExternalName { name_and_args } if name_and_args.is_empty()));
assert_eq!(
format!("{:?}", prog.to_command(&helper::Action::Store("egal".into()))),
format!(r#""{git}" "credential-" "store""#),
Expand Down
2 changes: 1 addition & 1 deletion gix-date/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ pub(crate) mod function {
let mut offset_in_seconds = hours * 3600 + minutes * 60;
if sign == Sign::Minus {
offset_in_seconds *= -1;
};
}
let time = Time {
seconds,
offset: offset_in_seconds,
Expand Down
42 changes: 20 additions & 22 deletions gix-diff/src/blob/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ impl Pipeline {
&& header.size > self.options.large_file_threshold_bytes
{
is_binary = Some(true);
};
}
let data = if is_binary == Some(true) {
Data::Binary { size: header.size }
} else {
Expand Down Expand Up @@ -461,27 +461,25 @@ impl Pipeline {
out.clear();
run_cmd(rela_path, cmd, out)?;
}
None => {
match res {
ToWorktreeOutcome::Unchanged(_) => {}
ToWorktreeOutcome::Buffer(src) => {
out.clear();
out.try_reserve(src.len())?;
out.extend_from_slice(src);
}
ToWorktreeOutcome::Process(MaybeDelayed::Immediate(mut stream)) => {
std::io::copy(&mut stream, out).map_err(|err| {
convert_to_diffable::Error::StreamCopy {
rela_path: rela_path.to_owned(),
source: err,
}
})?;
}
ToWorktreeOutcome::Process(MaybeDelayed::Delayed(_)) => {
unreachable!("we prohibit this")
}
};
}
None => match res {
ToWorktreeOutcome::Unchanged(_) => {}
ToWorktreeOutcome::Buffer(src) => {
out.clear();
out.try_reserve(src.len())?;
out.extend_from_slice(src);
}
ToWorktreeOutcome::Process(MaybeDelayed::Immediate(mut stream)) => {
std::io::copy(&mut stream, out).map_err(|err| {
convert_to_diffable::Error::StreamCopy {
rela_path: rela_path.to_owned(),
source: err,
}
})?;
}
ToWorktreeOutcome::Process(MaybeDelayed::Delayed(_)) => {
unreachable!("we prohibit this")
}
},
}
}

Expand Down
2 changes: 1 addition & 1 deletion gix-diff/src/blob/platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ impl Platform {
/// * `mode` is the kind of object (only blobs and links are allowed)
/// * `rela_path` is the relative path as seen from the (work)tree root.
/// * `kind` identifies the side of the diff this resource will be used for.
/// A diff needs both `OldOrSource` *and* `NewOrDestination`.
/// A diff needs both `OldOrSource` *and* `NewOrDestination`.
/// * `objects` provides access to the object database in case the resource can't be read from a worktree.
///
/// Note that it's assumed that either `id + mode (` or `rela_path` can serve as unique identifier for the resource,
Expand Down
Loading
Loading