Skip to main content

forge_primitives/
git.rs

1//! Git introspection data shapes.
2//!
3//! Type-only - branch resolution + diff scanning live in
4//! `forge_agent::env::git_diff`. `GitBranch` is the wire shape
5//! re-used by both the agent-side snapshot and a view's render.
6
7use serde::{Deserialize, Serialize};
8
9/// Branch resolution states emitted by the git-diff scanner.
10#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
11#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
12pub enum GitBranch {
13    /// Branch is named - `.git/HEAD` resolved to `refs/heads/<name>`.
14    Named(String),
15    /// `.git/HEAD` points at a commit hash directly (detached HEAD).
16    Detached,
17    /// `cwd` isn't inside any git tree.
18    #[default]
19    NoRepo,
20    /// Repo discovered but `.git/HEAD` couldn't be read or parsed.
21    Unknown,
22}
23
24/// How git classified one file's change.
25///
26/// Lives here rather than in the scanner because it crosses: the
27/// snapshot a view reads and the row it draws carry the
28/// same glyph, and one shape for both is what keeps them agreeing.
29#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
30#[serde(rename_all = "snake_case")]
31pub enum FileStatus {
32    #[default]
33    Modified,
34    Added,
35    Deleted,
36    Renamed,
37    Copied,
38    /// File mode changed (regular to symlink, file to submodule).
39    Typechange,
40    /// Unmerged - caught mid-merge-conflict.
41    Unmerged,
42    Untracked,
43}
44
45/// One commit of a branch's chain ahead of its default branch, as the
46/// chain draws it: the short sha, the subject line, the files the commit
47/// itself changed - the same shape a layer's stats take, bounded the same
48/// way - and when it was committed, unix seconds, so a reader can tell
49/// the newest from the oldest without counting positions.
50#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
51pub struct GitCommit {
52    pub sha: String,
53    pub subject: String,
54    pub stats: crate::git_diff::GitDiffStats,
55    pub time: u64,
56}
57
58/// Open pull request associated with the current branch. Populated
59/// by the git-diff scanner via `gh pr list --head <branch>`; surfaces
60/// in the Inspector pane's GIT section as the `PR #N` row.
61#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
62pub struct GitPrInfo {
63    pub number: u64,
64    pub url: String,
65    /// Whether GitHub reports it as a draft; an open PR is one or the
66    /// other, and the row says which.
67    #[serde(default)]
68    pub draft: bool,
69}
70
71/// Issue closed by an open PR (parsed from `closingIssuesReferences`
72/// on the GraphQL response, which is more reliable than scanning the
73/// PR body for `Closes #N`). Surfaces in the Inspector pane's GIT
74/// section as part of the `PR #N → closes #M #K` row.
75#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
76pub struct GitIssueRef {
77    pub number: u64,
78    pub url: String,
79}