Skip to main content

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}