1use crate::Result;
3use git2::Repository;
4use std::path::Path;
5
6pub fn ensure_repo_exists(repo_path: &Path) -> Result<()> {
7 if !repo_path.exists() {
8 std::fs::create_dir_all(repo_path)?;
9 Repository::init(repo_path)?;
10 }
11 Ok(())
12}
13
14pub fn git_commit(repo_path: &Path, message: &str) -> Result<()> {
15 use std::process::Command;
16
17 let add_status = Command::new("git")
19 .args(["-C", &repo_path.to_string_lossy(), "add", "."])
20 .status()?;
21
22 if !add_status.success() {
23 return Err(crate::RstaskError::Other("git add failed".to_string()));
24 }
25
26 let diff_status = Command::new("git")
28 .args([
29 "-C",
30 &repo_path.to_string_lossy(),
31 "diff-index",
32 "--quiet",
33 "HEAD",
34 "--",
35 ])
36 .status();
37
38 if let Ok(status) = diff_status
40 && status.success()
41 {
42 eprintln!("No changes detected");
43 return Ok(());
44 }
45
46 let commit_status = Command::new("git")
48 .args([
49 "-C",
50 &repo_path.to_string_lossy(),
51 "commit",
52 "--no-gpg-sign",
53 "-m",
54 message,
55 ])
56 .status()?;
57
58 if !commit_status.success() {
59 return Err(crate::RstaskError::Other("git commit failed".to_string()));
60 }
61
62 Ok(())
63}
64
65pub fn git_pull(repo_path: &str) -> Result<()> {
66 let repo = Repository::open(repo_path)?;
67
68 let mut remote = repo.find_remote("origin")?;
70 remote.fetch(&["master"], None, None)?;
71
72 let fetch_head = repo.find_reference("FETCH_HEAD")?;
74 let fetch_commit = repo.reference_to_annotated_commit(&fetch_head)?;
75
76 let analysis = repo.merge_analysis(&[&fetch_commit])?;
77
78 if analysis.0.is_up_to_date() {
79 Ok(())
81 } else if analysis.0.is_fast_forward() {
82 let refname = "refs/heads/master";
84 let mut reference = repo.find_reference(refname)?;
85 reference.set_target(fetch_commit.id(), "Fast-Forward")?;
86 repo.set_head(refname)?;
87 repo.checkout_head(Some(git2::build::CheckoutBuilder::default().force()))?;
88 Ok(())
89 } else {
90 Err(crate::RstaskError::Git(git2::Error::from_str(
92 "merge required",
93 )))
94 }
95}
96
97pub fn git_push(repo_path: &str) -> Result<()> {
98 let repo = Repository::open(repo_path)?;
99 let mut remote = repo.find_remote("origin")?;
100
101 remote.push(&["refs/heads/master:refs/heads/master"], None)?;
103
104 Ok(())
105}
106
107pub fn git_reset(repo_path: &Path) -> Result<()> {
108 let repo = Repository::open(repo_path)?;
109
110 let head = repo.head()?;
112 let head_commit = head.peel_to_commit()?;
113
114 let parent = head_commit.parent(0).map_err(|_| {
115 crate::RstaskError::Git(git2::Error::from_str("no parent commit to reset to"))
116 })?;
117
118 repo.reset(parent.as_object(), git2::ResetType::Hard, None)?;
119 Ok(())
120}