feat: implement blame
command in helix-vcs
This commit is contained in:
parent
b38eae1f98
commit
4b6690fc0a
4 changed files with 85 additions and 2 deletions
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -509,6 +509,7 @@ checksum = "736f14636705f3a56ea52b553e67282519418d9a35bb1e90b3a9637a00296b68"
|
|||
dependencies = [
|
||||
"gix-actor",
|
||||
"gix-attributes",
|
||||
"gix-blame",
|
||||
"gix-command",
|
||||
"gix-commitgraph",
|
||||
"gix-config",
|
||||
|
@ -591,6 +592,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"
|
||||
|
|
|
@ -19,7 +19,7 @@ tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "p
|
|||
parking_lot = "0.12"
|
||||
arc-swap = { version = "1.7.1" }
|
||||
|
||||
gix = { version = "0.70.0", features = ["attributes", "status"], default-features = false, optional = true }
|
||||
gix = { version = "0.70.0", features = ["attributes", "status", "blame"], default-features = false, optional = true }
|
||||
imara-diff = "0.1.8"
|
||||
anyhow = "1"
|
||||
|
||||
|
|
|
@ -2,10 +2,11 @@ use anyhow::{bail, Context, Result};
|
|||
use arc_swap::ArcSwap;
|
||||
use gix::filter::plumbing::driver::apply::Delay;
|
||||
use std::io::Read;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use gix::bstr::ByteSlice;
|
||||
use gix::bstr::{BStr, ByteSlice};
|
||||
use gix::diff::Rewrites;
|
||||
use gix::dir::entry::Status;
|
||||
use gix::objs::tree::EntryKind;
|
||||
|
@ -125,6 +126,57 @@ fn open_repo(path: &Path) -> Result<ThreadSafeRepository> {
|
|||
Ok(res)
|
||||
}
|
||||
|
||||
pub struct BlameInformation {
|
||||
pub commit_hash: String,
|
||||
pub author_name: String,
|
||||
pub commit_date: String,
|
||||
pub commit_message: String,
|
||||
}
|
||||
|
||||
/// Emulates the result of running `git blame` from the command line.
|
||||
pub fn blame(file: &Path, range: std::ops::Range<u32>) -> Result<BlameInformation> {
|
||||
let repo_dir = get_repo_dir(file)?;
|
||||
let repo = open_repo(repo_dir)
|
||||
.context("failed to open git repo")?
|
||||
.to_thread_local();
|
||||
|
||||
let suspect = repo.head()?.peel_to_commit_in_place()?;
|
||||
let traverse = gix::traverse::commit::topo::Builder::from_iters(
|
||||
&repo.objects,
|
||||
[suspect.id],
|
||||
None::<Vec<gix::ObjectId>>,
|
||||
)
|
||||
.build()?;
|
||||
let mut resource_cache = repo.diff_resource_cache_for_tree_diff()?;
|
||||
let latest_commit_id = gix::blame::file(
|
||||
&repo.objects,
|
||||
traverse,
|
||||
&mut resource_cache,
|
||||
BStr::new(file.as_os_str().as_bytes()),
|
||||
Some(range),
|
||||
)?
|
||||
.entries
|
||||
.first()
|
||||
.context("No commits found")?
|
||||
.commit_id;
|
||||
|
||||
let commit = repo.find_commit(latest_commit_id)?;
|
||||
|
||||
let author = commit.author()?;
|
||||
|
||||
let commit_date = author.time.format(gix::date::time::format::SHORT);
|
||||
let author_name = author.name.to_string();
|
||||
let commit_hash = commit.short_id()?.to_string();
|
||||
let commit_message = commit.message()?.title.to_string();
|
||||
|
||||
Ok(BlameInformation {
|
||||
commit_hash,
|
||||
author_name,
|
||||
commit_date,
|
||||
commit_message,
|
||||
})
|
||||
}
|
||||
|
||||
/// Emulates the result of running `git status` from the command line.
|
||||
fn status(repo: &Repository, f: impl Fn(Result<FileChange>) -> bool) -> Result<()> {
|
||||
let work_dir = repo
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use anyhow::{anyhow, bail, Result};
|
||||
use arc_swap::ArcSwap;
|
||||
use git::BlameInformation;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
|
@ -48,6 +49,12 @@ impl DiffProviderRegistry {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn blame(&self, file: &Path, range: std::ops::Range<u32>) -> Option<BlameInformation> {
|
||||
self.providers
|
||||
.iter()
|
||||
.find_map(|provider| provider.blame(file, range.clone()).ok())
|
||||
}
|
||||
|
||||
/// Fire-and-forget changed file iteration. Runs everything in a background task. Keeps
|
||||
/// iteration until `on_change` returns `false`.
|
||||
pub fn for_each_changed_file(
|
||||
|
@ -108,6 +115,14 @@ impl DiffProvider {
|
|||
}
|
||||
}
|
||||
|
||||
fn blame(&self, file: &Path, range: std::ops::Range<u32>) -> Result<BlameInformation> {
|
||||
match self {
|
||||
#[cfg(feature = "git")]
|
||||
Self::Git => git::blame(file, range),
|
||||
Self::None => bail!("No blame support compiled in"),
|
||||
}
|
||||
}
|
||||
|
||||
fn for_each_changed_file(
|
||||
&self,
|
||||
cwd: &Path,
|
||||
|
|
Loading…
Add table
Reference in a new issue