1use {
2 super::err::InitError,
3 crate::{
4 init::{select, sep, SEP},
5 utils::generate_random_string,
6 },
7 git2::{BranchType, Cred, CredentialType, IndexAddOption, Repository, Signature, Time},
8 inquire::{Password, Text},
9 std::{
10 borrow::Cow,
11 fmt::Write,
12 path::Path,
13 time::{SystemTime, UNIX_EPOCH},
14 },
15};
16
17
18pub fn auth(_: &str, username: Option<&str>, _: CredentialType) -> Result<Cred, git2::Error> {
20 println!("{}", InitError::CloneNeedsAuthentification);
21 sep();
22
23 let selection = select("Authenticate with", vec!["Key", "Password"])
24 .map_err(|_| git2::Error::from_str("Manual Exit."))?;
25
26 let username = if let Some(username) = username {
27 Cow::Borrowed(username)
28 } else {
29 let username = Text::new("Username:")
30 .prompt()
31 .map_err(|_| git2::Error::from_str("Manual Exit."))?;
32
33 Cow::Owned(username)
34 };
35
36 let credentials = match selection {
37 0 => {
38 let mut path_to_key: Option<String> = None;
39
40 while path_to_key.is_none() {
41 let imaginary_path = Text::new("Path to key:")
42 .prompt()
43 .map_err(|_| git2::Error::from_str("Manual Exit."))?;
44
45 if Path::new(&imaginary_path).exists() {
46 path_to_key = Some(imaginary_path);
47 } else {
48 eprintln!("{}", InitError::InvalidPath(imaginary_path));
49 sep();
50 }
51 }
52
53 Cred::ssh_key(&username, None, Path::new(&path_to_key.unwrap()), None)
54 },
55 1 => {
56 let mut password: Option<String> = None;
57
58 while password.is_none() {
59 let imaginary_path = Password::new("Password:")
60 .without_confirmation()
61 .prompt()
62 .map_err(|_| git2::Error::from_str("Manual Exit."))?;
63 password = Some(imaginary_path);
64 }
65
66 Cred::userpass_plaintext(&username, &password.unwrap())
67 },
68 _ => unreachable!(),
69 };
70
71 credentials
72}
73
74
75
76pub fn use_branch(repo_path: &str, branch_name: &str) -> Result<String, git2::Error> {
78 let repo = Repository::open(repo_path)?;
79
80 let branch = repo.find_branch(&format!("origin/{branch_name}"), BranchType::Remote)?;
81
82 let (object, _) = repo
84 .revparse_ext(&format!("origin/{branch_name}"))
85 .expect("Object not found");
86 repo.reset(&object, git2::ResetType::Hard, None)?;
87
88 let target_commit = branch.get().peel_to_commit()?;
90 let _ = repo.branch(branch_name, &target_commit, false);
92
93 let branch_ref = format!("refs/heads/{branch_name}");
94 let branch_ref = repo.find_reference(&branch_ref)?;
95 repo.set_head(branch_ref.name().unwrap_or_default())?;
96
97
98 let branch_to_keep = branch_name;
100 let branches = repo.branches(Some(BranchType::Local))?;
101 for branch in branches {
102 let (mut branch, _) = branch?;
103 let curr_branch_name = branch.name()?.unwrap_or_default();
104 if curr_branch_name != branch_to_keep {
105 branch.delete()?;
106 }
107 }
108
109 repo.remote_delete("origin")?;
111 let mut branch = repo.find_branch(branch_to_keep, BranchType::Local)?;
112 branch.rename("main", true)?;
113
114 let last_commit_oid = target_commit.id();
115 let mut last_commit_sha = String::with_capacity(40);
116 for byte in last_commit_oid.as_bytes() {
117 write!(&mut last_commit_sha, "{:02x}", byte).unwrap();
118 }
119
120
121 Ok(last_commit_sha)
122}
123
124
125
126pub fn clone(url: &str) -> Result<String, InitError> {
127 let clone_to = generate_random_string(64);
128
129 let mut callbacks = git2::RemoteCallbacks::new();
130 callbacks.credentials(auth);
131
132 let mut opts = git2::FetchOptions::new();
133 opts.remote_callbacks(callbacks);
134 opts.download_tags(git2::AutotagOption::All);
135
136 let mut builder = git2::build::RepoBuilder::new();
137 builder.fetch_options(opts);
138
139
140 match builder.clone(url, Path::new(&clone_to)) {
141 Ok(_repo) => Ok(clone_to),
142 Err(why) => {
143 match why.code() {
144 git2::ErrorCode::Auth => {
145 println!("{}", InitError::BadCredentials);
146 println!("{SEP}");
147 clone(url)
148 },
149 _ => Err(InitError::OtherGitError(why)),
150 }
151 },
152 }
153}
154
155
156pub fn first_commit(repo_path: &str) -> Result<(), git2::Error> {
159 let repo = Repository::open(repo_path)?;
160 let mut index = repo.index()?;
161 index.add_all(["*"].iter(), IndexAddOption::DEFAULT, None)?;
162 index.write()?;
163
164
165 let now = SystemTime::now()
166 .duration_since(UNIX_EPOCH)
167 .expect("UNIX_EPOCH is always a valid date.")
168 .as_secs();
169
170 let author = Signature::new("Wini", "wini", &Time::new(now as i64, 0))?;
171
172 let tree_id = index.write_tree()?;
173 let tree = repo.find_tree(tree_id)?;
174
175 let parent_ids = repo.head()?.peel_to_commit()?;
176
177 repo.commit(
178 Some("HEAD"),
179 &author,
180 &author,
181 "Creation of project",
182 &tree,
183 &[&parent_ids],
184 )?;
185
186
187 Ok(())
188}