fix: show revision-aware blame in diff buffers
Diff and snapshot sources were excluded before scheduling, and the separate diff renderer never painted attribution. Capture per-side Git provenance, preserve private object stores for historical tabs, and reuse the current-line worker and cache.
This commit is contained in:
+70
-5
@@ -1,7 +1,7 @@
|
||||
//! Read-only active-repository discovery and isolated revision comparison.
|
||||
use crate::diff::{
|
||||
Catalog, Category, ChangedFile, Comparison, ComparisonMode, FileStatus, PullRequestPage,
|
||||
RepoKind, Revision, RevisionTarget, line_hunks,
|
||||
BlameRevision, Catalog, Category, ChangedFile, Comparison, ComparisonMode, FileStatus,
|
||||
PullRequestPage, RepoKind, Revision, RevisionTarget, line_hunks,
|
||||
};
|
||||
use std::{
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
@@ -28,7 +28,8 @@ const WORKING_SNAPSHOT_LIMIT: usize = 512 * 1024 * 1024;
|
||||
const TIMEOUT: Duration = Duration::from_secs(60);
|
||||
static NEXT_TEMP: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
struct Temporary(PathBuf);
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(crate) struct Temporary(PathBuf);
|
||||
impl Temporary {
|
||||
fn new() -> Result<Self, String> {
|
||||
for _ in 0..100 {
|
||||
@@ -1054,6 +1055,7 @@ struct PreparedComparison {
|
||||
selected: [Revision; 2],
|
||||
selected_ids: [Option<String>; 2],
|
||||
revisions: [String; 2],
|
||||
blame: [Option<BlameRevision>; 2],
|
||||
mode: ComparisonMode,
|
||||
left: Snapshot,
|
||||
right: Snapshot,
|
||||
@@ -1077,7 +1079,7 @@ pub struct Repository {
|
||||
root: PathBuf,
|
||||
kind: RepoKind,
|
||||
store: PathBuf,
|
||||
temporary: Temporary,
|
||||
temporary: Arc<Temporary>,
|
||||
private: Option<(PathBuf, PathBuf)>, // workspace and Git object store
|
||||
remotes: Vec<Remote>,
|
||||
remote_refs: BTreeMap<(String, String), String>,
|
||||
@@ -1128,7 +1130,7 @@ impl Repository {
|
||||
root,
|
||||
kind,
|
||||
store,
|
||||
temporary: Temporary::new()?,
|
||||
temporary: Arc::new(Temporary::new()?),
|
||||
private: None,
|
||||
remotes: vec![],
|
||||
remote_refs: BTreeMap::new(),
|
||||
@@ -2390,6 +2392,48 @@ impl Repository {
|
||||
}
|
||||
Ok(Some(text(checked(output)?)?.trim().into()))
|
||||
}
|
||||
fn blame_baseline(&mut self, target: &RevisionTarget) -> Result<Option<BlameRevision>, String> {
|
||||
let source = if *target == RevisionTarget::Working {
|
||||
let id = if self.kind == RepoKind::Git {
|
||||
self.working_anchor()?
|
||||
} else {
|
||||
let parents = text(self.jj(
|
||||
&self.root,
|
||||
&[
|
||||
"log",
|
||||
"--no-graph",
|
||||
"-r",
|
||||
"@-",
|
||||
"-T",
|
||||
"commit_id ++ \"\\n\"",
|
||||
],
|
||||
)?)?;
|
||||
let mut parents = parents.lines();
|
||||
let parent = parents.next();
|
||||
// A merge has no single attribution baseline.
|
||||
if parents.next().is_some() {
|
||||
None
|
||||
} else {
|
||||
parent.map(str::to_owned)
|
||||
}
|
||||
};
|
||||
id.map(|id| (self.store.clone(), id))
|
||||
} else {
|
||||
Some(self.resolve(target)?)
|
||||
};
|
||||
Ok(source
|
||||
.filter(|(_, id)| !id.bytes().all(|byte| byte == b'0'))
|
||||
.map(|(store, commit)| BlameRevision {
|
||||
keepalive: self
|
||||
.private
|
||||
.as_ref()
|
||||
.filter(|(_, private_store)| private_store == &store)
|
||||
.map(|_| Arc::clone(&self.temporary)),
|
||||
store,
|
||||
commit,
|
||||
}))
|
||||
}
|
||||
|
||||
fn working_advanced(&self, before: Option<&str>, after: Option<&str>) -> Result<bool, String> {
|
||||
if before == after || after.is_none() {
|
||||
return Ok(false);
|
||||
@@ -2492,11 +2536,16 @@ impl Repository {
|
||||
} else {
|
||||
self.snapshot_revision(after_target, &right)?
|
||||
};
|
||||
let blame = [
|
||||
self.blame_baseline(before_target)?,
|
||||
self.blame_baseline(after_target)?,
|
||||
];
|
||||
let mut prepared = PreparedComparison {
|
||||
id: comparison_id,
|
||||
selected: [before.clone(), after.clone()],
|
||||
selected_ids,
|
||||
revisions: [before_revision, after_revision],
|
||||
blame,
|
||||
mode,
|
||||
left,
|
||||
right,
|
||||
@@ -2614,10 +2663,12 @@ impl Repository {
|
||||
}
|
||||
let right = self.working_snapshot(buffers)?;
|
||||
let revision = self.snapshot_revision(&RevisionTarget::Working, &right)?;
|
||||
let blame = self.blame_baseline(&RevisionTarget::Working)?;
|
||||
let mut prepared = self.comparison.take().unwrap();
|
||||
let previous_right = std::mem::replace(&mut prepared.right, right);
|
||||
let previous_files = std::mem::take(&mut prepared.files);
|
||||
let previous_revision = std::mem::replace(&mut prepared.revisions[1], revision);
|
||||
let previous_blame = std::mem::replace(&mut prepared.blame[1], blame);
|
||||
let result = self
|
||||
.build_comparison(&mut prepared, false)
|
||||
.and_then(|comparison| {
|
||||
@@ -2640,6 +2691,7 @@ impl Repository {
|
||||
prepared.right = previous_right;
|
||||
prepared.files = previous_files;
|
||||
prepared.revisions[1] = previous_revision;
|
||||
prepared.blame[1] = previous_blame;
|
||||
}
|
||||
self.comparison = Some(prepared);
|
||||
result.map(|comparison| Some(CheckoutRefresh::Checkout(comparison)))
|
||||
@@ -2671,13 +2723,16 @@ impl Repository {
|
||||
&& follow_working
|
||||
&& self.working_advanced(prepared.working_anchor.as_deref(), anchor.as_deref())?;
|
||||
let mut revisions = prepared.revisions.clone();
|
||||
let mut blame = prepared.blame.clone();
|
||||
let (left, before_label) = if branch {
|
||||
let base = self.merge_base(ids[0].as_deref().unwrap(), ids[1].as_deref().unwrap())?;
|
||||
let label = format!("merge base of {label} and {after_label} · {base}");
|
||||
revisions[0] = base.clone();
|
||||
blame[0] = self.blame_baseline(&RevisionTarget::Commit(base.clone()))?;
|
||||
(Some(self.snapshot(&RevisionTarget::Commit(base))?), label)
|
||||
} else if changed_before {
|
||||
revisions[0] = ids[0].clone().unwrap();
|
||||
blame[0] = self.blame_baseline(&RevisionTarget::Commit(revisions[0].clone()))?;
|
||||
(
|
||||
Some(self.snapshot(&RevisionTarget::Commit(ids[0].clone().unwrap()))?),
|
||||
label,
|
||||
@@ -2699,6 +2754,13 @@ impl Repository {
|
||||
ids[1].clone().unwrap()
|
||||
};
|
||||
}
|
||||
if right.is_some() {
|
||||
blame[1] = self.blame_baseline(&if advance_working {
|
||||
RevisionTarget::Working
|
||||
} else {
|
||||
RevisionTarget::Commit(revisions[1].clone())
|
||||
})?;
|
||||
}
|
||||
let pinned_checkout = ids[1]
|
||||
.as_deref()
|
||||
.map(|id| self.pinned_checkout(id))
|
||||
@@ -2711,6 +2773,7 @@ impl Repository {
|
||||
let previous_before = std::mem::replace(&mut prepared.before, before_label);
|
||||
let previous_editable = prepared.editable_after;
|
||||
let previous_revisions = std::mem::replace(&mut prepared.revisions, revisions);
|
||||
let previous_blame = std::mem::replace(&mut prepared.blame, blame);
|
||||
prepared.editable_after =
|
||||
working_after || (pinned_checkout.is_some() && pinned_checkout == anchor);
|
||||
let result = self
|
||||
@@ -2747,6 +2810,7 @@ impl Repository {
|
||||
prepared.before = previous_before;
|
||||
prepared.editable_after = previous_editable;
|
||||
prepared.revisions = previous_revisions;
|
||||
prepared.blame = previous_blame;
|
||||
}
|
||||
self.comparison = Some(prepared);
|
||||
result
|
||||
@@ -2861,6 +2925,7 @@ impl Repository {
|
||||
};
|
||||
Comparison {
|
||||
revisions: prepared.revisions.clone(),
|
||||
blame: prepared.blame.clone(),
|
||||
before: prepared.before.clone(),
|
||||
after: prepared.after.clone(),
|
||||
files,
|
||||
|
||||
Reference in New Issue
Block a user