From 37750830495895ae44f41c9d5a9f8dc55746147d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Tue, 1 Apr 2025 19:02:36 +0200 Subject: [PATCH 1/7] Draft PoC of blame using gitoxide --- Cargo.lock | 186 +++++++++++++++++++++++++++++++++++++ asyncgit/Cargo.toml | 2 + asyncgit/src/sync/blame.rs | 149 ++++++++++++++--------------- 3 files changed, 264 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2abe61b64a..59f577f290 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -174,6 +174,7 @@ dependencies = [ "git2", "git2-hooks", "gix", + "gix-blame", "invalidstring", "log", "openssl-sys", @@ -675,6 +676,20 @@ dependencies = [ "syn", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "der" version = "0.7.9" @@ -816,6 +831,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "env_filter" version = "0.1.3" @@ -1212,22 +1236,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a61e71ec6817fc3c9f12f812682cfe51ee6ea0d2e27e02fc3849c35524617435" dependencies = [ "gix-actor", + "gix-attributes", + "gix-command", "gix-commitgraph", "gix-config", "gix-date", "gix-diff", "gix-discover", "gix-features", + "gix-filter", "gix-fs", "gix-glob", "gix-hash", "gix-hashtable", + "gix-ignore", "gix-index", "gix-lock", "gix-object", "gix-odb", "gix-pack", "gix-path", + "gix-pathspec", "gix-protocol", "gix-ref", "gix-refspec", @@ -1235,12 +1264,14 @@ dependencies = [ "gix-revwalk", "gix-sec", "gix-shallow", + "gix-submodule", "gix-tempfile", "gix-trace", "gix-traverse", "gix-url", "gix-utils", "gix-validate", + "gix-worktree", "once_cell", "smallvec", "thiserror 2.0.12", @@ -1260,6 +1291,23 @@ dependencies = [ "winnow", ] +[[package]] +name = "gix-attributes" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f151000bf662ef5f641eca6102d942ee31ace80f271a3ef642e99776ce6ddb38" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "gix-quote", + "gix-trace", + "kstring", + "smallvec", + "thiserror 2.0.12", + "unicode-bom", +] + [[package]] name = "gix-bitmap" version = "0.2.14" @@ -1269,6 +1317,21 @@ dependencies = [ "thiserror 2.0.12", ] +[[package]] +name = "gix-blame" +version = "0.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc795e239a2347eb50ed18b8c529382dd8b62439c57277f79af3d8f8928a986" +dependencies = [ + "gix-diff", + "gix-hash", + "gix-object", + "gix-trace", + "gix-traverse", + "gix-worktree", + "thiserror 2.0.12", +] + [[package]] name = "gix-chunk" version = "0.4.11" @@ -1357,8 +1420,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2c975dad2afc85e4e233f444d1efbe436c3cdcf3a07173984509c436d00a3f8" dependencies = [ "bstr", + "gix-command", + "gix-filter", + "gix-fs", "gix-hash", "gix-object", + "gix-path", + "gix-tempfile", + "gix-trace", + "gix-traverse", + "gix-worktree", + "imara-diff", "thiserror 2.0.12", ] @@ -1398,6 +1470,27 @@ dependencies = [ "walkdir", ] +[[package]] +name = "gix-filter" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdcc36cd7dbc63ed0ec3558645886553d1afd3cd09daa5efb9cba9cceb942bbb" +dependencies = [ + "bstr", + "encoding_rs", + "gix-attributes", + "gix-command", + "gix-hash", + "gix-object", + "gix-packetline-blocking", + "gix-path", + "gix-quote", + "gix-trace", + "gix-utils", + "smallvec", + "thiserror 2.0.12", +] + [[package]] name = "gix-fs" version = "0.14.0" @@ -1447,6 +1540,19 @@ dependencies = [ "parking_lot", ] +[[package]] +name = "gix-ignore" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f529dcb80bf9855c0a7c49f0ac588df6d6952d63a63fefc254b9c869d2cdf6f" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "gix-trace", + "unicode-bom", +] + [[package]] name = "gix-index" version = "0.39.0" @@ -1559,6 +1665,18 @@ dependencies = [ "thiserror 2.0.12", ] +[[package]] +name = "gix-packetline-blocking" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cbf8767c6abd5a6779f586702b5bcd8702380f4208219449cf1c9d0cd1e17c" +dependencies = [ + "bstr", + "faster-hex", + "gix-trace", + "thiserror 2.0.12", +] + [[package]] name = "gix-path" version = "0.10.15" @@ -1572,6 +1690,21 @@ dependencies = [ "thiserror 2.0.12", ] +[[package]] +name = "gix-pathspec" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6430d3a686c08e9d59019806faa78c17315fe22ae73151a452195857ca02f86c" +dependencies = [ + "bitflags 2.9.0", + "bstr", + "gix-attributes", + "gix-config-value", + "gix-glob", + "gix-path", + "thiserror 2.0.12", +] + [[package]] name = "gix-protocol" version = "0.49.0" @@ -1694,12 +1827,28 @@ dependencies = [ "thiserror 2.0.12", ] +[[package]] +name = "gix-submodule" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74972fe8d46ac8a09490ae1e843b4caf221c5b157c5ac17057e8e1c38417a3ac" +dependencies = [ + "bstr", + "gix-config", + "gix-path", + "gix-pathspec", + "gix-refspec", + "gix-url", + "thiserror 2.0.12", +] + [[package]] name = "gix-tempfile" version = "17.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6de439bbb9a5d3550c9c7fab0e16d2d637d120fcbe0dfbc538772a187f099b" dependencies = [ + "dashmap", "gix-fs", "libc", "once_cell", @@ -1780,6 +1929,25 @@ dependencies = [ "thiserror 2.0.12", ] +[[package]] +name = "gix-worktree" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6673512f7eaa57a6876adceca6978a501d6c6569a4f177767dc405f8b9778958" +dependencies = [ + "bstr", + "gix-attributes", + "gix-features", + "gix-fs", + "gix-glob", + "gix-hash", + "gix-ignore", + "gix-index", + "gix-object", + "gix-path", + "gix-validate", +] + [[package]] name = "group" version = "0.13.0" @@ -2004,6 +2172,15 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "imara-diff" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17d34b7d42178945f775e84bc4c36dde7c1c6cdfea656d3354d009056f2bb3d2" +dependencies = [ + "hashbrown 0.15.2", +] + [[package]] name = "indexmap" version = "2.9.0" @@ -2177,6 +2354,15 @@ dependencies = [ "libc", ] +[[package]] +name = "kstring" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1" +dependencies = [ + "static_assertions", +] + [[package]] name = "lazy_static" version = "1.5.0" diff --git a/asyncgit/Cargo.toml b/asyncgit/Cargo.toml index 7966e82e5c..b2d4223a62 100644 --- a/asyncgit/Cargo.toml +++ b/asyncgit/Cargo.toml @@ -22,7 +22,9 @@ git2-hooks = { path = "../git2-hooks", version = ">=0.4" } gix = { version = "0.71.0", default-features = false, features = [ "max-performance", "revision", + "blob-diff" ] } +gix-blame = "0.0.0" log = "0.4" # git2 = { path = "../../extern/git2-rs", features = ["vendored-openssl"]} # git2 = { git="https://github.com/extrawurst/git2-rs.git", rev="fc13dcc", features = ["vendored-openssl"]} diff --git a/asyncgit/src/sync/blame.rs b/asyncgit/src/sync/blame.rs index 19f125f6b3..0772e4f1a4 100644 --- a/asyncgit/src/sync/blame.rs +++ b/asyncgit/src/sync/blame.rs @@ -1,15 +1,9 @@ //! Sync git API for fetching a file blame use super::{utils, CommitId, RepoPath}; -use crate::{ - error::{Error, Result}, - sync::{get_commits_info, repository::repo}, -}; -use git2::BlameOptions; +use crate::{error::Result, sync::get_commits_info}; use scopetime::scope_time; use std::collections::{HashMap, HashSet}; -use std::io::{BufRead, BufReader}; -use std::path::Path; /// A `BlameHunk` contains all the information that will be shown to the user. #[derive(Clone, Hash, Debug, PartialEq, Eq)] @@ -40,19 +34,6 @@ pub struct FileBlame { pub lines: Vec<(Option, String)>, } -/// fixup `\` windows path separators to git compatible `/` -fn fixup_windows_path(path: &str) -> String { - #[cfg(windows)] - { - path.replace('\\', "/") - } - - #[cfg(not(windows))] - { - path.to_string() - } -} - /// pub fn blame_file( repo_path: &RepoPath, @@ -61,35 +42,52 @@ pub fn blame_file( ) -> Result { scope_time!("blame_file"); - let repo = repo(repo_path)?; + let repo: gix::Repository = + gix::ThreadSafeRepository::discover_with_environment_overrides(repo_path.gitpath()) + .map(Into::into)?; + let tip: gix::ObjectId = match commit_id { + Some(commit_id) => gix::ObjectId::from_bytes_or_panic( + commit_id.get_oid().as_bytes(), + ), + _ => repo.head()?.peel_to_commit_in_place()?.id, + }; + let traverse = gix::traverse::commit::topo::Builder::from_iters( + &repo.objects, + [tip], + None::>, + ) + .build() + .expect("TODO"); + + let mut resource_cache = + repo.diff_resource_cache_for_tree_diff().expect("TODO"); + + let outcome = gix_blame::file( + &repo.objects, + traverse, + &mut resource_cache, + file_path.into(), + None, + ) + .expect("TODO"); let commit_id = if let Some(commit_id) = commit_id { commit_id } else { + let repo = crate::sync::repo(repo_path)?; + utils::get_head_repo(&repo)? }; - let spec = - format!("{}:{}", commit_id, fixup_windows_path(file_path)); - - let object = repo.revparse_single(&spec)?; - let blob = repo.find_blob(object.id())?; - - if blob.is_binary() { - return Err(Error::NoBlameOnBinaryFile); - } - - let mut opts = BlameOptions::new(); - opts.newest_commit(commit_id.into()); - - let blame = - repo.blame_file(Path::new(file_path), Some(&mut opts))?; - - let reader = BufReader::new(blob.content()); - - let unique_commit_ids: HashSet<_> = blame + let unique_commit_ids: HashSet<_> = outcome + .entries .iter() - .map(|hunk| CommitId::new(hunk.final_commit_id())) + .map(|entry| { + CommitId::new( + git2::Oid::from_bytes(entry.commit_id.as_bytes()) + .expect("TODO"), + ) + }) .collect(); let mut commit_ids = Vec::with_capacity(unique_commit_ids.len()); commit_ids.extend(unique_commit_ids); @@ -100,40 +98,45 @@ pub fn blame_file( .map(|commit_info| (commit_info.id, commit_info)) .collect(); - let lines: Vec<(Option, String)> = reader - .lines() - .enumerate() - .map(|(i, line)| { - // Line indices in a `FileBlame` are 1-based. - let corresponding_hunk = blame.get_line(i + 1); - - if let Some(hunk) = corresponding_hunk { - let commit_id = CommitId::new(hunk.final_commit_id()); - // Line indices in a `BlameHunk` are 1-based. - let start_line = - hunk.final_start_line().saturating_sub(1); - let end_line = - start_line.saturating_add(hunk.lines_in_hunk()); - - if let Some(commit_info) = - unique_commit_infos.get(&commit_id) - { - let hunk = BlameHunk { - commit_id, - author: commit_info.author.clone(), - time: commit_info.time, - start_line, - end_line, + // TODO + // The shape of data as returned by `entries_with_lines` is preferable to the one chosen here + // because the former is much closer to what the UI is going to need in the end. + let lines: Vec<(Option, String)> = outcome + .entries_with_lines() + .flat_map(|(entry, lines)| { + let commit_id = CommitId::new( + git2::Oid::from_bytes(entry.commit_id.as_bytes()) + .expect("TODO"), + ); + let start_in_blamed_file = + entry.start_in_blamed_file as usize; + + lines + .iter() + .enumerate() + .map(|(i, line)| { + // TODO + let trimmed_line = + line.to_string().trim_end().to_string(); + + if let Some(commit_info) = + unique_commit_infos.get(&commit_id) + { + return ( + Some(BlameHunk { + commit_id, + author: commit_info.author.clone(), + time: commit_info.time, + start_line: start_in_blamed_file + i, + end_line: start_in_blamed_file + i, + }), + trimmed_line, + ); }; - return ( - Some(hunk), - line.unwrap_or_else(|_| String::new()), - ); - } - } - - (None, line.unwrap_or_else(|_| String::new())) + (None, trimmed_line) + }) + .collect::>() }) .collect(); From 007bda7935ab5d096b35efaaf303c66bfae9f487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Thu, 3 Apr 2025 15:42:14 +0200 Subject: [PATCH 2/7] Replace expect by ? Add missing `+ 1` to make test pass. --- asyncgit/src/error.rs | 22 ++++++++++++++++++++++ asyncgit/src/sync/blame.rs | 33 +++++++++++++++++---------------- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/asyncgit/src/error.rs b/asyncgit/src/error.rs index 2113e93d6e..a22ed1036a 100644 --- a/asyncgit/src/error.rs +++ b/asyncgit/src/error.rs @@ -113,6 +113,20 @@ pub enum Error { #[error("gix::revision::walk error: {0}")] GixRevisionWalk(#[from] gix::revision::walk::Error), + /// + #[error("gix::traverse::commit::topo error: {0}")] + GixTraverseCommitTopo(#[from] gix::traverse::commit::topo::Error), + + /// + #[error("gix::repository::diff_resource_cache error: {0}")] + GixRepositoryDiffResourceCache( + #[from] Box, + ), + + /// + #[error("gix_blame error: {0}")] + GixBlame(#[from] gix_blame::Error), + /// #[error("amend error: config commit.gpgsign=true detected.\ngpg signing is not supported for amending non-last commits")] SignAmendNonLastCommit, @@ -146,3 +160,11 @@ impl From for Error { Self::GixDiscover(Box::new(error)) } } + +impl From for Error { + fn from( + error: gix::repository::diff_resource_cache::Error, + ) -> Self { + Self::GixRepositoryDiffResourceCache(Box::new(error)) + } +} diff --git a/asyncgit/src/sync/blame.rs b/asyncgit/src/sync/blame.rs index 0772e4f1a4..b2c1820086 100644 --- a/asyncgit/src/sync/blame.rs +++ b/asyncgit/src/sync/blame.rs @@ -34,6 +34,15 @@ pub struct FileBlame { pub lines: Vec<(Option, String)>, } +fn object_id_to_oid(object_id: gix::ObjectId) -> git2::Oid { + // TODO + // This should not fail. It will also become obsolete once `gix::ObjectId` is used throughout + // `gitui`. + #[allow(clippy::expect_used)] + git2::Oid::from_bytes(object_id.as_bytes()) + .expect("ObjectId could not be converted to Oid") +} + /// pub fn blame_file( repo_path: &RepoPath, @@ -56,11 +65,10 @@ pub fn blame_file( [tip], None::>, ) - .build() - .expect("TODO"); + .build()?; let mut resource_cache = - repo.diff_resource_cache_for_tree_diff().expect("TODO"); + repo.diff_resource_cache_for_tree_diff()?; let outcome = gix_blame::file( &repo.objects, @@ -68,8 +76,7 @@ pub fn blame_file( &mut resource_cache, file_path.into(), None, - ) - .expect("TODO"); + )?; let commit_id = if let Some(commit_id) = commit_id { commit_id @@ -82,12 +89,7 @@ pub fn blame_file( let unique_commit_ids: HashSet<_> = outcome .entries .iter() - .map(|entry| { - CommitId::new( - git2::Oid::from_bytes(entry.commit_id.as_bytes()) - .expect("TODO"), - ) - }) + .map(|entry| CommitId::new(object_id_to_oid(entry.commit_id))) .collect(); let mut commit_ids = Vec::with_capacity(unique_commit_ids.len()); commit_ids.extend(unique_commit_ids); @@ -104,10 +106,8 @@ pub fn blame_file( let lines: Vec<(Option, String)> = outcome .entries_with_lines() .flat_map(|(entry, lines)| { - let commit_id = CommitId::new( - git2::Oid::from_bytes(entry.commit_id.as_bytes()) - .expect("TODO"), - ); + let commit_id = + CommitId::new(object_id_to_oid(entry.commit_id)); let start_in_blamed_file = entry.start_in_blamed_file as usize; @@ -128,7 +128,8 @@ pub fn blame_file( author: commit_info.author.clone(), time: commit_info.time, start_line: start_in_blamed_file + i, - end_line: start_in_blamed_file + i, + end_line: start_in_blamed_file + + i + 1, }), trimmed_line, ); From 604d03239e24d29b0a81c52afed5274aaabb9138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Wed, 16 Apr 2025 20:30:26 +0200 Subject: [PATCH 3/7] Update to gix-blame 0.1.0 --- Cargo.lock | 36 ++++++++++++++++++++---------------- asyncgit/Cargo.toml | 2 +- asyncgit/src/sync/blame.rs | 21 +++++++++++++-------- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 59f577f290..440ee9f89a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1293,9 +1293,9 @@ dependencies = [ [[package]] name = "gix-attributes" -version = "0.24.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f151000bf662ef5f641eca6102d942ee31ace80f271a3ef642e99776ce6ddb38" +checksum = "e4e25825e0430aa11096f8b65ced6780d4a96a133f81904edceebb5344c8dd7f" dependencies = [ "bstr", "gix-glob", @@ -1319,16 +1319,20 @@ dependencies = [ [[package]] name = "gix-blame" -version = "0.0.0" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc795e239a2347eb50ed18b8c529382dd8b62439c57277f79af3d8f8928a986" +checksum = "b25d5aa111ce9cb6d087c2d1153b96553a3dc491163398e96622b0bd7b40c7c6" dependencies = [ + "gix-commitgraph", + "gix-date", "gix-diff", "gix-hash", "gix-object", + "gix-revwalk", "gix-trace", "gix-traverse", "gix-worktree", + "smallvec", "thiserror 2.0.12", ] @@ -1472,9 +1476,9 @@ dependencies = [ [[package]] name = "gix-filter" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdcc36cd7dbc63ed0ec3558645886553d1afd3cd09daa5efb9cba9cceb942bbb" +checksum = "cb2b2bbffdc5cc9b2b82fc82da1b98163c9b423ac2b45348baa83a947ac9ab89" dependencies = [ "bstr", "encoding_rs", @@ -1542,9 +1546,9 @@ dependencies = [ [[package]] name = "gix-ignore" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f529dcb80bf9855c0a7c49f0ac588df6d6952d63a63fefc254b9c869d2cdf6f" +checksum = "9a27c8380f493a10d1457f756a3f81924d578fc08d6535e304dfcafbf0261d18" dependencies = [ "bstr", "gix-glob", @@ -1667,9 +1671,9 @@ dependencies = [ [[package]] name = "gix-packetline-blocking" -version = "0.18.2" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cbf8767c6abd5a6779f586702b5bcd8702380f4208219449cf1c9d0cd1e17c" +checksum = "1ecf3ea2e105c7e45587bac04099824301262a6c43357fad5205da36dbb233b3" dependencies = [ "bstr", "faster-hex", @@ -1692,9 +1696,9 @@ dependencies = [ [[package]] name = "gix-pathspec" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6430d3a686c08e9d59019806faa78c17315fe22ae73151a452195857ca02f86c" +checksum = "fef8422c3c9066d649074b24025125963f85232bfad32d6d16aea9453b82ec14" dependencies = [ "bitflags 2.9.0", "bstr", @@ -1829,9 +1833,9 @@ dependencies = [ [[package]] name = "gix-submodule" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74972fe8d46ac8a09490ae1e843b4caf221c5b157c5ac17057e8e1c38417a3ac" +checksum = "78c7390c2059505c365e9548016d4edc9f35749c6a9112b7b1214400bbc68da2" dependencies = [ "bstr", "gix-config", @@ -1931,9 +1935,9 @@ dependencies = [ [[package]] name = "gix-worktree" -version = "0.39.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6673512f7eaa57a6876adceca6978a501d6c6569a4f177767dc405f8b9778958" +checksum = "f7760dbc4b79aa274fed30adc0d41dca6b917641f26e7867c4071b1fb4dc727b" dependencies = [ "bstr", "gix-attributes", diff --git a/asyncgit/Cargo.toml b/asyncgit/Cargo.toml index b2d4223a62..b591620efb 100644 --- a/asyncgit/Cargo.toml +++ b/asyncgit/Cargo.toml @@ -24,7 +24,7 @@ gix = { version = "0.71.0", default-features = false, features = [ "revision", "blob-diff" ] } -gix-blame = "0.0.0" +gix-blame = "0.1.0" log = "0.4" # git2 = { path = "../../extern/git2-rs", features = ["vendored-openssl"]} # git2 = { git="https://github.com/extrawurst/git2-rs.git", rev="fc13dcc", features = ["vendored-openssl"]} diff --git a/asyncgit/src/sync/blame.rs b/asyncgit/src/sync/blame.rs index b2c1820086..d9e2d55b8c 100644 --- a/asyncgit/src/sync/blame.rs +++ b/asyncgit/src/sync/blame.rs @@ -60,22 +60,27 @@ pub fn blame_file( ), _ => repo.head()?.peel_to_commit_in_place()?.id, }; - let traverse = gix::traverse::commit::topo::Builder::from_iters( - &repo.objects, - [tip], - None::>, - ) - .build()?; + let cache: Option = + repo.commit_graph_if_enabled().expect("TODO"); let mut resource_cache = repo.diff_resource_cache_for_tree_diff()?; + let diff_algorithm = repo.diff_algorithm().expect("TODO"); + + let options = gix_blame::Options { + diff_algorithm, + range: None, + since: None, + }; + let outcome = gix_blame::file( &repo.objects, - traverse, + tip.into(), + cache, &mut resource_cache, file_path.into(), - None, + options, )?; let commit_id = if let Some(commit_id) = commit_id { From 0056126efaa7f8b83ce75ea6f35b53e5182fc40c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Thu, 17 Apr 2025 08:58:25 +0200 Subject: [PATCH 4/7] Replace expect by ? --- asyncgit/src/error.rs | 12 ++++++++++++ asyncgit/src/sync/blame.rs | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/asyncgit/src/error.rs b/asyncgit/src/error.rs index a22ed1036a..e226b18cd2 100644 --- a/asyncgit/src/error.rs +++ b/asyncgit/src/error.rs @@ -123,6 +123,18 @@ pub enum Error { #[from] Box, ), + /// + #[error("gix::repository::commit_graph_if_enabled error: {0}")] + GixRepositoryCommitGraphIfEnabled( + #[from] gix::repository::commit_graph_if_enabled::Error, + ), + + /// + #[error("gix::config::diff::algorithm error: {0}")] + GixConfigDiffAlgorithm( + #[from] gix::config::diff::algorithm::Error, + ), + /// #[error("gix_blame error: {0}")] GixBlame(#[from] gix_blame::Error), diff --git a/asyncgit/src/sync/blame.rs b/asyncgit/src/sync/blame.rs index d9e2d55b8c..9dd2b8b4f7 100644 --- a/asyncgit/src/sync/blame.rs +++ b/asyncgit/src/sync/blame.rs @@ -62,11 +62,11 @@ pub fn blame_file( }; let cache: Option = - repo.commit_graph_if_enabled().expect("TODO"); + repo.commit_graph_if_enabled()?; let mut resource_cache = repo.diff_resource_cache_for_tree_diff()?; - let diff_algorithm = repo.diff_algorithm().expect("TODO"); + let diff_algorithm = repo.diff_algorithm()?; let options = gix_blame::Options { diff_algorithm, From a0f35155644015f4c31e934f7e8ac287bf0fb5b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Thu, 17 Apr 2025 08:58:38 +0200 Subject: [PATCH 5/7] Remove unnecessary into --- asyncgit/src/sync/blame.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asyncgit/src/sync/blame.rs b/asyncgit/src/sync/blame.rs index 9dd2b8b4f7..0bedbe1ab2 100644 --- a/asyncgit/src/sync/blame.rs +++ b/asyncgit/src/sync/blame.rs @@ -76,7 +76,7 @@ pub fn blame_file( let outcome = gix_blame::file( &repo.objects, - tip.into(), + tip, cache, &mut resource_cache, file_path.into(), From 48f0a89c8c0f44958b9f06d80017dc45a2100a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Thu, 17 Apr 2025 09:48:50 +0200 Subject: [PATCH 6/7] Remove unnecessary semicolon --- asyncgit/src/sync/blame.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asyncgit/src/sync/blame.rs b/asyncgit/src/sync/blame.rs index 0bedbe1ab2..8ff5971cb0 100644 --- a/asyncgit/src/sync/blame.rs +++ b/asyncgit/src/sync/blame.rs @@ -138,7 +138,7 @@ pub fn blame_file( }), trimmed_line, ); - }; + } (None, trimmed_line) }) From 40069397f3c560487a65458336984db66fc260e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Thu, 17 Apr 2025 17:19:05 +0200 Subject: [PATCH 7/7] Convert to unix separators on Windows --- asyncgit/src/sync/blame.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/asyncgit/src/sync/blame.rs b/asyncgit/src/sync/blame.rs index 8ff5971cb0..5043c2012c 100644 --- a/asyncgit/src/sync/blame.rs +++ b/asyncgit/src/sync/blame.rs @@ -51,6 +51,10 @@ pub fn blame_file( ) -> Result { scope_time!("blame_file"); + let file_path: &gix::bstr::BStr = file_path.into(); + let file_path = + gix::path::to_unix_separators_on_windows(file_path); + let repo: gix::Repository = gix::ThreadSafeRepository::discover_with_environment_overrides(repo_path.gitpath()) .map(Into::into)?; @@ -79,7 +83,7 @@ pub fn blame_file( tip, cache, &mut resource_cache, - file_path.into(), + &file_path, options, )?; @@ -148,7 +152,7 @@ pub fn blame_file( let file_blame = FileBlame { commit_id, - path: file_path.into(), + path: file_path.to_string(), lines, };