strop_git/revision.rs
1//! Revision-addressed locations (0014 wave 4): permalinks, jumps
2//! into history, blame parent-hops all speak this.
3
4use std::path::{Path, PathBuf};
5
6use crate::repo::Repo;
7
8/// A revisioned source location (0014 wave 4): permalinks, jumps into
9/// history, and blame's parent-hop all speak this — no more "permalink
10/// from a historical view links HEAD's file".
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct SourceLocation {
13 pub revision: GitRevision,
14 /// Repo-relative path.
15 pub path: PathBuf,
16 /// 1-based line range, when the location is a selection.
17 pub lines: Option<(usize, usize)>,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub enum GitRevision {
22 /// The checked-out branch head.
23 Head,
24 /// A specific commit (surfaces carry this).
25 Commit(String),
26 /// The staged content (the index) — 0018's four-state model makes
27 /// it a first-class revision, not an implicit middle.
28 Index,
29 /// The on-disk worktree file.
30 Worktree,
31 /// The merge-base of two commits (review starts here).
32 MergeBase(String, String),
33}
34
35impl GitRevision {
36 /// Read a file's bytes AT this revision. Live (the editor's buffer)
37 /// never crosses this seam — the editor owns that copy.
38 pub fn read(&self, repo: &Repo, rel: &Path) -> Option<Vec<u8>> {
39 match self {
40 GitRevision::Head => repo.head_bytes(rel),
41 GitRevision::Commit(sha) => repo.commit_bytes(sha, rel),
42 GitRevision::Index => repo.index_bytes(rel),
43 GitRevision::Worktree => std::fs::read(repo.workdir.join(rel)).ok(),
44 GitRevision::MergeBase(a, b) => {
45 let base = repo.merge_base(a, b)?;
46 repo.commit_bytes(&base, rel)
47 }
48 }
49 }
50}
51
52impl SourceLocation {
53 /// The URL slug: a pinned commit sha or the branch's name.
54 pub fn revision_slug(&self) -> String {
55 match &self.revision {
56 GitRevision::Head => "HEAD".into(),
57 GitRevision::Commit(sha) => sha.clone(),
58 GitRevision::Index => "index".into(),
59 GitRevision::Worktree => "worktree".into(),
60 GitRevision::MergeBase(a, b) => format!("{a}...{b}"),
61 }
62 }
63}