upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/lib/git/identify_ahead_behind.rs
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2024-09-04 11:32:05 +0100
committerDanConwayDev <DanConwayDev@protonmail.com>2024-09-04 14:23:54 +0100
commit771f944af447c202eba045936a36dee71ab797ac (patch)
treee691de4ebc8dde7ac4855e139881ff923bc254ce /src/lib/git/identify_ahead_behind.rs
parent949c6459aa7683453a7160423b689ceadb08954b (diff)
refactor: fix imports, etc based on restructure
move some functions out of ngit and into lib/mod and lib/git_events remove MockConnect from binaries so it is only used in the library. this was done: * mainly because automocks were not being imported from lib into each binary * but also because the these functions were being tested with MockConnect
Diffstat (limited to 'src/lib/git/identify_ahead_behind.rs')
-rw-r--r--src/lib/git/identify_ahead_behind.rs196
1 files changed, 196 insertions, 0 deletions
diff --git a/src/lib/git/identify_ahead_behind.rs b/src/lib/git/identify_ahead_behind.rs
new file mode 100644
index 0000000..c98c994
--- /dev/null
+++ b/src/lib/git/identify_ahead_behind.rs
@@ -0,0 +1,196 @@
1use anyhow::{Context, Result};
2use nostr_sdk::hashes::sha1::Hash as Sha1Hash;
3
4use super::{Repo, RepoActions};
5
6/**
7 * returns `(from_branch,to_branch,ahead,behind)`
8 */
9pub fn identify_ahead_behind(
10 git_repo: &Repo,
11 from_branch: &Option<String>,
12 to_branch: &Option<String>,
13) -> Result<(String, String, Vec<Sha1Hash>, Vec<Sha1Hash>)> {
14 let (from_branch, from_tip) = match from_branch {
15 Some(name) => (
16 name.to_string(),
17 git_repo
18 .get_tip_of_branch(name)
19 .context(format!("cannot find from_branch '{name}'"))?,
20 ),
21 None => (
22 if let Ok(name) = git_repo.get_checked_out_branch_name() {
23 name
24 } else {
25 "head".to_string()
26 },
27 git_repo
28 .get_head_commit()
29 .context("failed to get head commit")
30 .context(
31 "checkout a commit or specify a from_branch. head does not reveal a commit",
32 )?,
33 ),
34 };
35
36 let (to_branch, to_tip) = match to_branch {
37 Some(name) => (
38 name.to_string(),
39 git_repo
40 .get_tip_of_branch(name)
41 .context(format!("cannot find to_branch '{name}'"))?,
42 ),
43 None => {
44 let (name, commit) = git_repo
45 .get_main_or_master_branch()
46 .context("the default branches (main or master) do not exist")?;
47 (name.to_string(), commit)
48 }
49 };
50
51 match git_repo.get_commits_ahead_behind(&to_tip, &from_tip) {
52 Err(e) => {
53 if e.to_string().contains("is not an ancestor of") {
54 return Err(e).context(format!(
55 "'{from_branch}' is not branched from '{to_branch}'"
56 ));
57 }
58 Err(e).context(format!(
59 "failed to get commits ahead and behind from '{from_branch}' to '{to_branch}'"
60 ))
61 }
62 Ok((ahead, behind)) => Ok((from_branch, to_branch, ahead, behind)),
63 }
64}
65
66#[cfg(test)]
67mod tests {
68
69 use test_utils::git::GitTestRepo;
70
71 use super::*;
72 use crate::git::oid_to_sha1;
73
74 #[test]
75 fn when_from_branch_doesnt_exist_return_error() -> Result<()> {
76 let test_repo = GitTestRepo::default();
77 let git_repo = Repo::from_path(&test_repo.dir)?;
78
79 test_repo.populate()?;
80 let branch_name = "doesnt_exist";
81 assert_eq!(
82 identify_ahead_behind(&git_repo, &Some(branch_name.to_string()), &None)
83 .unwrap_err()
84 .to_string(),
85 format!("cannot find from_branch '{}'", &branch_name),
86 );
87 Ok(())
88 }
89
90 #[test]
91 fn when_to_branch_doesnt_exist_return_error() -> Result<()> {
92 let test_repo = GitTestRepo::default();
93 let git_repo = Repo::from_path(&test_repo.dir)?;
94
95 test_repo.populate()?;
96 let branch_name = "doesnt_exist";
97 assert_eq!(
98 identify_ahead_behind(&git_repo, &None, &Some(branch_name.to_string()))
99 .unwrap_err()
100 .to_string(),
101 format!("cannot find to_branch '{}'", &branch_name),
102 );
103 Ok(())
104 }
105
106 #[test]
107 fn when_to_branch_is_none_and_no_main_or_master_branch_return_error() -> Result<()> {
108 let test_repo = GitTestRepo::new("notmain")?;
109 let git_repo = Repo::from_path(&test_repo.dir)?;
110
111 test_repo.populate()?;
112
113 assert_eq!(
114 identify_ahead_behind(&git_repo, &None, &None)
115 .unwrap_err()
116 .to_string(),
117 "the default branches (main or master) do not exist",
118 );
119 Ok(())
120 }
121
122 #[test]
123 fn when_from_branch_is_not_head_return_as_from_branch() -> Result<()> {
124 let test_repo = GitTestRepo::default();
125 let git_repo = Repo::from_path(&test_repo.dir)?;
126
127 test_repo.populate()?;
128 // create feature branch with 1 commit ahead
129 test_repo.create_branch("feature")?;
130 test_repo.checkout("feature")?;
131 std::fs::write(test_repo.dir.join("t3.md"), "some content")?;
132 let head_oid = test_repo.stage_and_commit("add t3.md")?;
133
134 // make feature branch 1 commit behind
135 test_repo.checkout("main")?;
136 std::fs::write(test_repo.dir.join("t4.md"), "some content")?;
137 let main_oid = test_repo.stage_and_commit("add t4.md")?;
138
139 let (from_branch, to_branch, ahead, behind) =
140 identify_ahead_behind(&git_repo, &Some("feature".to_string()), &None)?;
141
142 assert_eq!(from_branch, "feature");
143 assert_eq!(ahead, vec![oid_to_sha1(&head_oid)]);
144 assert_eq!(to_branch, "main");
145 assert_eq!(behind, vec![oid_to_sha1(&main_oid)]);
146 Ok(())
147 }
148
149 #[test]
150 fn when_to_branch_is_not_main_return_as_to_branch() -> Result<()> {
151 let test_repo = GitTestRepo::default();
152 let git_repo = Repo::from_path(&test_repo.dir)?;
153
154 test_repo.populate()?;
155 // create dev branch with 1 commit ahead
156 test_repo.create_branch("dev")?;
157 test_repo.checkout("dev")?;
158 std::fs::write(test_repo.dir.join("t3.md"), "some content")?;
159 let dev_oid_first = test_repo.stage_and_commit("add t3.md")?;
160
161 // create feature branch with 1 commit ahead of dev
162 test_repo.create_branch("feature")?;
163 test_repo.checkout("feature")?;
164 std::fs::write(test_repo.dir.join("t4.md"), "some content")?;
165 let feature_oid = test_repo.stage_and_commit("add t4.md")?;
166
167 // make feature branch 1 behind
168 test_repo.checkout("dev")?;
169 std::fs::write(test_repo.dir.join("t3.md"), "some content")?;
170 let dev_oid = test_repo.stage_and_commit("add t3.md")?;
171
172 let (from_branch, to_branch, ahead, behind) = identify_ahead_behind(
173 &git_repo,
174 &Some("feature".to_string()),
175 &Some("dev".to_string()),
176 )?;
177
178 assert_eq!(from_branch, "feature");
179 assert_eq!(ahead, vec![oid_to_sha1(&feature_oid)]);
180 assert_eq!(to_branch, "dev");
181 assert_eq!(behind, vec![oid_to_sha1(&dev_oid)]);
182
183 let (from_branch, to_branch, ahead, behind) =
184 identify_ahead_behind(&git_repo, &Some("feature".to_string()), &None)?;
185
186 assert_eq!(from_branch, "feature");
187 assert_eq!(
188 ahead,
189 vec![oid_to_sha1(&feature_oid), oid_to_sha1(&dev_oid_first)]
190 );
191 assert_eq!(to_branch, "main");
192 assert_eq!(behind, vec![]);
193
194 Ok(())
195 }
196}