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}