feat: add local diff review workflow

This commit is contained in:
Valdemar Lorenzen
2026-10-16 13:10:07 +02:00
parent 930735c537
commit 0446ec86c7
15 changed files with 1871 additions and 7 deletions
+52
View File
@@ -1058,6 +1058,7 @@ struct PreparedComparison {
id: u64,
selected: [Revision; 2],
selected_ids: [Option<String>; 2],
revisions: [String; 2],
mode: ComparisonMode,
left: Snapshot,
right: Snapshot,
@@ -2284,6 +2285,34 @@ impl Repository {
}
Ok(snapshot)
}
fn snapshot_revision(
&mut self,
target: &RevisionTarget,
snapshot: &Snapshot,
) -> Result<String, String> {
if let RevisionTarget::Commit(id) = target {
return Ok(id.clone());
}
self.ensure_private()?;
let store = &self.private.as_ref().unwrap().1;
let mut input = b"vamp-working-snapshot-v1\0".to_vec();
for (path, file) in snapshot {
self.check_cancelled()?;
for field in [
path.as_os_str().as_encoded_bytes(),
file.mode.as_bytes(),
file.bytes.as_slice(),
file.notice.as_deref().unwrap_or("").as_bytes(),
] {
input.extend_from_slice(&(field.len() as u64).to_be_bytes());
input.extend_from_slice(field);
}
}
let mut command = git_command(store);
command.args(["hash-object", "--no-filters", "--stdin"]);
let id = text(checked(run(command, input, &self.cancelled)?)?)?;
Ok(format!("working:{}", id.trim()))
}
fn snapshot(&mut self, target: &RevisionTarget) -> Result<Snapshot, String> {
if *target == RevisionTarget::Working {
self.working_snapshot(&[])
@@ -2454,10 +2483,17 @@ impl Repository {
let (store, id) = pinned.as_ref().unwrap();
self.commit_snapshot(store, id)?
};
let before_revision = self.snapshot_revision(before_target, &left)?;
let after_revision = if same {
before_revision.clone()
} else {
self.snapshot_revision(after_target, &right)?
};
let mut prepared = PreparedComparison {
id: comparison_id,
selected: [before.clone(), after.clone()],
selected_ids,
revisions: [before_revision, after_revision],
mode,
left,
right,
@@ -2574,9 +2610,11 @@ impl Repository {
return Ok(None);
}
let right = self.working_snapshot(buffers)?;
let revision = self.snapshot_revision(&RevisionTarget::Working, &right)?;
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 result = self
.build_comparison(&mut prepared, false)
.and_then(|comparison| {
@@ -2598,6 +2636,7 @@ impl Repository {
} else {
prepared.right = previous_right;
prepared.files = previous_files;
prepared.revisions[1] = previous_revision;
}
self.comparison = Some(prepared);
result.map(|comparison| Some(CheckoutRefresh::Checkout(comparison)))
@@ -2628,11 +2667,14 @@ impl Repository {
let advance_working = working_after
&& follow_working
&& self.working_advanced(prepared.working_anchor.as_deref(), anchor.as_deref())?;
let mut revisions = prepared.revisions.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();
(Some(self.snapshot(&RevisionTarget::Commit(base))?), label)
} else if changed_before {
revisions[0] = ids[0].clone().unwrap();
(
Some(self.snapshot(&RevisionTarget::Commit(ids[0].clone().unwrap()))?),
label,
@@ -2647,6 +2689,13 @@ impl Repository {
} else {
None
};
if let Some(right) = &right {
revisions[1] = if advance_working {
self.snapshot_revision(&RevisionTarget::Working, right)?
} else {
ids[1].clone().unwrap()
};
}
let pinned_checkout = ids[1]
.as_deref()
.map(|id| self.pinned_checkout(id))
@@ -2658,6 +2707,7 @@ impl Repository {
let previous_files = std::mem::take(&mut prepared.files);
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);
prepared.editable_after =
working_after || (pinned_checkout.is_some() && pinned_checkout == anchor);
let result = self
@@ -2693,6 +2743,7 @@ impl Repository {
prepared.files = previous_files;
prepared.before = previous_before;
prepared.editable_after = previous_editable;
prepared.revisions = previous_revisions;
}
self.comparison = Some(prepared);
result
@@ -2810,6 +2861,7 @@ impl Repository {
BTreeSet::new()
};
Comparison {
revisions: prepared.revisions.clone(),
before: prepared.before.clone(),
after: prepared.after.clone(),
files,