From f88cd81d8790230c14bec01a7e6b13da7fd5a613 Mon Sep 17 00:00:00 2001 From: dawn Date: Sat, 24 Oct 2026 06:55:44 +0300 Subject: [PATCH] gitmirror/xrpc: share the change sides between diffs and patches Signed-off-by: dawn --- gitmirror/crates/gitmirror-xrpc/src/diff.rs | 104 ++++++++---------- .../crates/gitmirror-xrpc/src/format_patch.rs | 27 +---- 2 files changed, 46 insertions(+), 85 deletions(-) diff --git a/gitmirror/crates/gitmirror-xrpc/src/diff.rs b/gitmirror/crates/gitmirror-xrpc/src/diff.rs index 7823e5484..a3d7b0338 100644 --- a/gitmirror/crates/gitmirror-xrpc/src/diff.rs +++ b/gitmirror/crates/gitmirror-xrpc/src/diff.rs @@ -1,6 +1,7 @@ use gix::ObjectId; -use gix::bstr::ByteSlice as _; -use gix::objs::tree::EntryKind; +use gix::bstr::{BStr, ByteSlice as _}; +use gix::object::tree::diff::ChangeDetached; +use gix::objs::tree::{EntryKind, EntryMode}; use line_numbers::{LineNumber, SingleLineSpan}; use rustc_hash::FxHashSet; use tracing::info; @@ -128,7 +129,7 @@ pub fn diff<'repo>( move |repo, change, old, new, cache| { // Submodules (gitlinks) can't go through the blob pipeline — their commit lives in // another repo. Emit the commit oids directly for the caller to compare. - if old.is_kind(EntryKind::Commit) || new.is_kind(EntryKind::Commit) { + if is_kind(old, EntryKind::Commit) || is_kind(new, EntryKind::Commit) { return Ok(submodule_diff(repo, change, old, new)); } build_diff(repo, change, cache, embed_content) @@ -146,9 +147,9 @@ fn changes<'repo, T, F>( where F: FnMut( &gix::Repository, - &gix::object::tree::diff::ChangeDetached, - Side, - Side, + &ChangeDetached, + Option>, + Option>, &mut gix::diff::blob::Platform, ) -> anyhow::Result, { @@ -175,9 +176,9 @@ impl Iterator for ChangeIter<'_, F> where F: FnMut( &gix::Repository, - &gix::object::tree::diff::ChangeDetached, - Side, - Side, + &ChangeDetached, + Option>, + Option>, &mut gix::diff::blob::Platform, ) -> anyhow::Result, { @@ -186,9 +187,9 @@ where fn next(&mut self) -> Option { loop { let change = self.changes.next()?; - let (old, new) = change_sides(&change); + let (old, new) = sides(&change); // Trees are recursed into by gix and never diffed directly; skip defensively. - if old.is_kind(EntryKind::Tree) || new.is_kind(EntryKind::Tree) { + if is_kind(old, EntryKind::Tree) || is_kind(new, EntryKind::Tree) { continue; } let result = (self.build)(self.repo, &change, old, new, &mut self.cache); @@ -198,65 +199,48 @@ where } } -enum Side { - Absent, - Present { oid: ObjectId, kind: EntryKind }, +#[derive(Clone, Copy)] +pub(crate) struct Side<'a> { + pub(crate) path: &'a BStr, + pub(crate) mode: EntryMode, + pub(crate) id: ObjectId, } -impl Side { - fn is_kind(&self, wanted: EntryKind) -> bool { - matches!(self, Side::Present { kind, .. } if *kind == wanted) - } -} - -/// Decompose a change into its `(old, new)` sides regardless of variant. -fn change_sides(change: &gix::object::tree::diff::ChangeDetached) -> (Side, Side) { - use gix::object::tree::diff::ChangeDetached as C; - let present = |oid, mode: gix::object::tree::EntryMode| Side::Present { - oid, - kind: mode.kind(), +pub(crate) fn sides(change: &ChangeDetached) -> (Option>, Option>) { + let side = |path, (mode, id): (EntryMode, &gix::oid)| Side { + path, + mode, + id: id.to_owned(), }; + let old = side(change.source_location(), change.source_entry_mode_and_id()); + let new = side(change.location(), change.entry_mode_and_id()); match change { - C::Addition { id, entry_mode, .. } => (Side::Absent, present(*id, *entry_mode)), - C::Deletion { id, entry_mode, .. } => (present(*id, *entry_mode), Side::Absent), - C::Modification { - previous_id, - previous_entry_mode, - id, - entry_mode, - .. - } => ( - present(*previous_id, *previous_entry_mode), - present(*id, *entry_mode), - ), - C::Rewrite { - source_id, - source_entry_mode, - id, - entry_mode, - .. - } => ( - present(*source_id, *source_entry_mode), - present(*id, *entry_mode), - ), + ChangeDetached::Addition { .. } => (None, Some(new)), + ChangeDetached::Deletion { .. } => (Some(old), None), + ChangeDetached::Modification { .. } | ChangeDetached::Rewrite { .. } => { + (Some(old), Some(new)) + } } } +fn is_kind(side: Option>, kind: EntryKind) -> bool { + side.is_some_and(|side| side.mode.kind() == kind) +} + /// Build a [`Diff`] for a submodule (gitlink) change: no hunks, both sides carry their commit /// oid with `is_submodule` set. The caller compares the oids as submodule pointers. fn submodule_diff( repo: &gix::Repository, - change: &gix::object::tree::diff::ChangeDetached, - old: Side, - new: Side, + change: &ChangeDetached, + old: Option>, + new: Option>, ) -> Diff { let path = change.location().to_string(); - let side = |s: Side| FileContent { + let side = |s: Option>| FileContent { path: path.clone(), - oid: match s { - Side::Absent => ObjectId::null(repo.object_hash()).to_string(), - Side::Present { oid, .. } => oid.to_string(), - }, + oid: s + .map_or_else(|| ObjectId::null(repo.object_hash()), |side| side.id) + .to_string(), size: 0, is_binary: false, is_submodule: true, @@ -276,9 +260,9 @@ fn submodule_diff( /// Count the changed lines of a single file; submodules count as a changed file with no lines. fn build_stat( repo: &gix::Repository, - change: &gix::object::tree::diff::ChangeDetached, - old: Side, - new: Side, + change: &ChangeDetached, + old: Option>, + new: Option>, cache: &mut gix::diff::blob::Platform, ) -> anyhow::Result { use gix::diff::blob::platform::prepare_diff::Operation; @@ -289,7 +273,7 @@ fn build_stat( files_changed: 1, ..Stat::default() }; - if old.is_kind(EntryKind::Commit) || new.is_kind(EntryKind::Commit) { + if is_kind(old, EntryKind::Commit) || is_kind(new, EntryKind::Commit) { return Ok(stat); } let blob = change.attach(repo, repo).diff(cache)?; diff --git a/gitmirror/crates/gitmirror-xrpc/src/format_patch.rs b/gitmirror/crates/gitmirror-xrpc/src/format_patch.rs index 67f8fe533..e96bb0511 100644 --- a/gitmirror/crates/gitmirror-xrpc/src/format_patch.rs +++ b/gitmirror/crates/gitmirror-xrpc/src/format_patch.rs @@ -8,10 +8,11 @@ use gix::diff::blob::sources::byte_lines; use gix::diff::blob::{Algorithm, Hunk, InternedInput, diff_with_slider_heuristics}; use gix::object::tree::diff::ChangeDetached; use gix::objs::TreeRefIter; -use gix::objs::tree::{EntryKind, EntryMode}; +use gix::objs::tree::EntryKind; use gix::prelude::ObjectIdExt as _; use gix::revision::walk::Sorting; +use crate::diff::{Side, sides}; use crate::error::XrpcError; const CONTEXT: u32 = 3; @@ -499,13 +500,6 @@ fn rename_name(a: &[u8], b: &[u8]) -> Vec { name } -#[derive(Clone, Copy)] -struct Side<'a> { - path: &'a BStr, - mode: EntryMode, - id: ObjectId, -} - impl Side<'_> { fn octal(&self) -> u32 { u32::from(self.mode.value()) @@ -516,23 +510,6 @@ impl Side<'_> { } } -fn sides(change: &ChangeDetached) -> (Option>, Option>) { - let side = |path, (mode, id): (EntryMode, &gix::oid)| Side { - path, - mode, - id: id.to_owned(), - }; - let old = side(change.source_location(), change.source_entry_mode_and_id()); - let new = side(change.location(), change.entry_mode_and_id()); - match change { - ChangeDetached::Addition { .. } => (None, Some(new)), - ChangeDetached::Deletion { .. } => (Some(old), None), - ChangeDetached::Modification { .. } | ChangeDetached::Rewrite { .. } => { - (Some(old), Some(new)) - } - } -} - /// (similarity percent, is a copy) fn rewrite(change: &ChangeDetached) -> Option<(u32, bool)> { match change { -- 2.54.0