Skip to main content

ironflow_ops_git/
lib.rs

1//! Git operations for Ironflow workflows, powered by [`git2`].
2//!
3//! This crate provides a comprehensive set of Git operations as Ironflow
4//! [`Operation`](ironflow_core::operation::Operation) implementations. Each
5//! operation wraps a [`git2`] API call, running it inside
6//! [`spawn_blocking`](tokio::task::spawn_blocking) since `git2` is synchronous.
7//!
8//! # Architecture
9//!
10//! - [`GitRepo`] is the central handle, wrapping a repository path
11//! - Each operation is a standalone struct implementing [`Operation`](ironflow_core::operation::Operation)
12//! - All operations return `kind() == "git"`
13//! - Parameters are set at construction time, not via [`OperationContext`](ironflow_core::operation::OperationContext)
14//!
15//! # Quick start
16//!
17//! ```no_run
18//! use ironflow_ops_git::GitRepo;
19//! use ironflow_ops_git::repository::RepoInit;
20//! use ironflow_ops_git::commit::CommitCreate;
21//! use ironflow_ops_git::index::IndexAdd;
22//! use ironflow_core::operation::{Operation, OperationContext, NoopSecretResolver};
23//! use std::sync::Arc;
24//!
25//! # async fn example() -> Result<(), ironflow_core::error::OperationError> {
26//! let ctx = OperationContext::new(Arc::new(NoopSecretResolver));
27//!
28//! // Initialize a repo
29//! let init = RepoInit::new("/tmp/my-repo", false);
30//! init.execute(&ctx).await?;
31//!
32//! // Stage a file and commit
33//! let add = IndexAdd::new("/tmp/my-repo", "README.md");
34//! add.execute(&ctx).await?;
35//!
36//! let commit = CommitCreate::new("/tmp/my-repo", "Initial commit", "Alice", "alice@example.com");
37//! commit.execute(&ctx).await?;
38//! # Ok(())
39//! # }
40//! ```
41//!
42//! # Tracked operations
43//!
44//! Every operation implements [`Operation`](ironflow_core::operation::Operation),
45//! so it can be passed to `WorkflowContext::operation()` for step lifecycle
46//! tracking (step record, status transitions, duration, output persistence).
47//!
48//! # Modules
49//!
50//! Operations are organized by Git domain:
51//!
52//! | Module | Operations |
53//! |--------|-----------|
54//! | [`repository`] | Init, Open, Clone, Discover, State |
55//! | [`index`] | Add, AddAll, Remove, RemoveAll, UpdateAll, WriteTree |
56//! | [`commit`] | Create, Find, Amend, Signed |
57//! | [`branch`] | Create, Delete, Rename, List, Lookup, IsHead, SetUpstream |
58//! | [`tag`] | CreateLightweight, CreateAnnotated, Delete, List, ListMatch |
59//! | [`remote`] | Create, Delete, Rename, SetUrl, List, Lookup |
60//! | [`fetch`] | Fetch, Push, Prune, DefaultBranch |
61//! | [`merge`] | Branch, Analysis, Commits, Base, CleanupState |
62//! | [`rebase`] | Init, Next, Commit, Abort, Finish |
63//! | [`cherrypick`] | Cherrypick, CherrypickCommit, Revert, RevertCommit |
64//! | [`stash`] | Save, Apply, Pop, Drop, List |
65//! | [`diff`] | TreeToTree, TreeToIndex, IndexToWorkdir, Stats, FindSimilar, Apply |
66//! | [`checkout`] | Head, Index, Tree |
67//! | [`blame`] | BlameFile |
68//! | [`log`] | RevwalkNew, RevwalkPushRange, RevwalkSimplifyFirstParent |
69//! | [`refs`] | Create, Delete, Rename, Lookup, NameToId |
70//! | [`reflog`] | Read, Append, Drop |
71//! | [`submodule`] | Add, Init, Update, Lookup, List |
72//! | [`worktree`] | Add, List, Validate, Prune |
73//! | [`config`] | Get, Set, Delete, List |
74//! | [`status`] | File, List, ShouldIgnore |
75//! | [`reset`] | Reset (soft, mixed, hard) |
76//! | [`graph`] | AheadBehind, DescendantOf, Describe |
77//! | [`object`] | BlobCreate, TreeLookup, FindObject |
78
79pub mod blame;
80pub mod branch;
81pub mod checkout;
82pub mod cherrypick;
83pub mod commit;
84pub mod config;
85pub mod diff;
86pub mod fetch;
87pub mod graph;
88mod helpers;
89pub mod index;
90pub mod log;
91pub mod merge;
92pub mod object;
93pub mod rebase;
94pub mod reflog;
95pub mod refs;
96pub mod remote;
97mod repo;
98pub mod repository;
99pub mod reset;
100pub mod stash;
101pub mod status;
102pub mod submodule;
103pub mod tag;
104pub mod worktree;
105
106pub use git2;
107pub use repo::GitRepo;
108
109#[cfg(test)]
110pub(crate) mod test_helpers {
111    use std::fs;
112    use std::path::Path;
113    use std::sync::Arc;
114
115    use git2::{Oid, Repository, Signature};
116    use ironflow_core::operation::{NoopSecretResolver, OperationContext};
117
118    pub(crate) fn ctx() -> OperationContext {
119        OperationContext::new(Arc::new(NoopSecretResolver))
120    }
121
122    pub(crate) fn init_repo(path: &Path) -> Oid {
123        let repo = Repository::init(path).unwrap();
124        fs::write(path.join("file.txt"), "content").unwrap();
125        let mut idx = repo.index().unwrap();
126        idx.add_path(Path::new("file.txt")).unwrap();
127        idx.write().unwrap();
128        let tree = repo.find_tree(idx.write_tree().unwrap()).unwrap();
129        let sig = Signature::now("Test", "test@test.com").unwrap();
130        repo.commit(Some("HEAD"), &sig, &sig, "init", &tree, &[])
131            .unwrap()
132    }
133
134    pub(crate) fn make_two_commits(path: &Path) -> (String, String) {
135        let repo = Repository::init(path).unwrap();
136        let sig = Signature::now("Test", "test@test.com").unwrap();
137        fs::write(path.join("file.txt"), "v1").unwrap();
138        let mut idx = repo.index().unwrap();
139        idx.add_path(Path::new("file.txt")).unwrap();
140        idx.write().unwrap();
141        let tree = repo.find_tree(idx.write_tree().unwrap()).unwrap();
142        let c1 = repo
143            .commit(Some("HEAD"), &sig, &sig, "first", &tree, &[])
144            .unwrap();
145        let parent = repo.find_commit(c1).unwrap();
146
147        fs::write(path.join("other.txt"), "v2").unwrap();
148        let mut idx = repo.index().unwrap();
149        idx.add_path(Path::new("other.txt")).unwrap();
150        idx.write().unwrap();
151        let tree2 = repo.find_tree(idx.write_tree().unwrap()).unwrap();
152        let c2 = repo
153            .commit(Some("HEAD"), &sig, &sig, "second", &tree2, &[&parent])
154            .unwrap();
155        (c1.to_string(), c2.to_string())
156    }
157}