upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/funcs/get_updates_of_patches.rs
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2023-05-21 11:14:47 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2023-05-21 11:14:47 +0000
commit0067804cc00e94ce2b7043e67f9ff50968525479 (patch)
tree2accdc6d4e9b73df4f20499238ec24f24a52a1b8 /src/funcs/get_updates_of_patches.rs
parent5c5feaa732363e32e2a980a887fa42b4394b1a5e (diff)
v0.0.1-alpha funcs
Diffstat (limited to 'src/funcs/get_updates_of_patches.rs')
-rw-r--r--src/funcs/get_updates_of_patches.rs263
1 files changed, 263 insertions, 0 deletions
diff --git a/src/funcs/get_updates_of_patches.rs b/src/funcs/get_updates_of_patches.rs
new file mode 100644
index 0000000..ad28774
--- /dev/null
+++ b/src/funcs/get_updates_of_patches.rs
@@ -0,0 +1,263 @@
1use std::{path::PathBuf, str::FromStr};
2
3use git2::Repository;
4use nostr::{Event, Filter, EventId};
5use nostr_sdk::blocking::Client;
6
7use crate::{ngit_tag::{tag_is_patch_parent, tag_is_initial_commit, tag_extract_value, tag_is_patch, tag_is_branch, tag_is_commit_parent, tag_is_commit}, utils::{load_event}, funcs::find_latest_patch::find_latest_patch, patch::{patch_commit_id, patch_is_commit}, branch_refs::BranchRefs, repo_config::RepoConfig, kind::Kind};
8
9
10/// ancessor patch events first
11pub fn get_updates_of_patches (
12 client: &Client,
13 branch_refs: &mut BranchRefs,
14 git_repo: &Repository,
15 repo_dir_path: &PathBuf,
16 branch_id: &String,
17 branch_name: &Option<String>,
18 pull_new_branch: bool,
19) -> Vec<Event> {
20
21 let repo_config = RepoConfig::open(repo_dir_path);
22 let last_patch_timestamp = repo_config.last_patch_update_time(branch_id.clone());
23
24 // create direct patches filter
25 let direct_patches_filter = Filter::new()
26 .event(
27 EventId::from_str(branch_id)
28 .expect("branch_id to render as EventId")
29 )
30 .kinds(vec![Kind::Patch.into_sdk_custom_kind()]);
31
32 let mut filters = vec![
33 match &last_patch_timestamp {
34 None => direct_patches_filter,
35 Some(timestamp) => {
36 direct_patches_filter.since(timestamp.clone())
37 }
38 }
39 ];
40
41 // get maintainers group
42 if branch_refs.maintainers_group(Some(&branch_id)).is_none() {
43 // fetch branch mantainers group and check again
44 client.add_relays(
45 branch_refs.branch_as_repo(Some(branch_id))
46 .relays
47 .clone().iter().map(|url| (url, None)).collect()
48 )
49 .expect("branch relays to be added to client");
50 let mut group_events = client.get_events_of(
51 vec![
52 // use the opportunity to get all the remaining groups
53 Filter::new().ids(branch_refs.group_ids_for_branches_without_cached_groups()),
54 ],
55 None,
56 )
57 .expect("get_events_of to not return an error");
58 group_events.sort();
59 group_events.dedup();
60 branch_refs.updates(group_events);
61 }
62
63 // create indirect pacthes filter
64 let merges_into_branch: Vec<Event> = branch_refs.merges.iter().filter(|event|
65 // merged into branch
66 event.tags.iter().any(|t|
67 tag_is_branch(t)
68 && tag_extract_value(t) == branch_id.clone()
69 )
70 // merge timestamp is after last_patch_timestamp - we already have patches before this date
71 && match &last_patch_timestamp {
72 None => true,
73 Some(timestamp) => timestamp < &event.created_at
74 }
75 // author is member of branch maintainers group
76 && branch_refs.is_authorized(Some(branch_id), &event.pubkey)
77 .expect("found group event for branch after checking on speficied relays")
78 ).map(|e|e.clone())
79 .collect();
80
81 if !merges_into_branch.is_empty() {
82 filters.push(
83 // ids for all patches referenced in merges
84 Filter::new()
85 .ids(
86 merges_into_branch.iter().flat_map(|event|
87 event.tags.iter()
88 .filter(|t| tag_is_patch(t))
89 .map(|t| tag_extract_value(t).clone())
90 .collect::<Vec<String>>()
91 )
92 .collect::<Vec<String>>()
93 )
94 // .kinds(vec![Kind::Patch.into_sdk_custom_kind()])
95 )
96 }
97
98 // find patch events
99 let mut patch_events: Vec<Event> = client.get_events_of(
100 filters,
101 None,
102 )
103 .expect("get_events_of to not return an error when looking for patches");
104
105 patch_events.sort();
106 patch_events.dedup();
107
108 // find patch tip on branch
109 let latest_patch_on_branch = match find_latest_patch(
110 &branch_id,
111 &patch_events,
112 &merges_into_branch,
113 &branch_refs,
114 &repo_dir_path,
115 ) {
116 // no patches return empty vector
117 None => { return vec![] }, // for pull_new_branch do we set the branch to the latest commit referneced even if we have it?
118 Some(event) => event,
119 };
120
121 let mut new_patches_on_branch = vec![];
122 // for pull_new_branch - cycle through patch parents until we find any patch that exists in our commit history
123 if pull_new_branch {
124 let mut patch_event_id = latest_patch_on_branch.id.to_string();
125 let mut patch_commit_id = tag_extract_value(
126 latest_patch_on_branch.tags.iter().find(|t|tag_is_commit(t))
127 .expect("all patch events to have a commit tag")
128 );
129
130 loop {
131 let patch = match patch_events.iter().find(|p| p.id.to_string() == patch_event_id.clone()) {
132 // patch event found in patch_events
133 Some(patch) => patch,
134 None => {
135 // loop for parent locally
136 if repo_dir_path.join(format!(
137 ".ngit/patches/{}.json",
138 patch_commit_id,
139 )).exists() {
140 // break out of loop when we identify the commit where the branch begins
141 break
142 }
143 else {
144 panic!("cannot find parent patch locally or in patch_events. This will fail if the branch does not share a commit with main / master")
145 }
146 }
147 };
148 // add patch to list of patches to apply to new branch
149 new_patches_on_branch.push(patch.clone());
150 // prepare loop for next patch - set patch_event_id to current patches parent
151 patch_event_id = tag_extract_value(
152 patch.tags.iter().find(|t|tag_is_patch_parent(t))
153 .expect("patch to always have a patch parent.")
154 );
155 patch_commit_id = tag_extract_value(
156 patch.tags.iter().find(|t|tag_is_commit_parent(t))
157 .expect("patch to always have a commit parent. This will fail if the branch does not share a commit with main / master")
158 );
159 };
160 }
161
162 // cycle through patch parents until we the latest commit in our local branch, or error if detects a rebase (it exists in our branch history)
163 else {
164 // revwalk through branch to identify forced push
165 let mut revwalk = git_repo.revwalk()
166 .expect("revwalk to not error on git_repo");
167 match &branch_name {
168 Some(name) => {
169 revwalk.push(
170 git_repo.find_branch(
171 name.as_str(),
172 git2::BranchType::Local
173 )
174 .expect("branch found from the branch_name")
175 .get()
176 .peel_to_commit()
177 .expect("branch reference to peel back to a commit")
178 .id()
179 )
180 .expect("revwalk push_glob(branch_name) not to error if branch name is not None");
181 }
182 None => (),
183 }
184 let commit_ids_in_branch: Vec<String> = if branch_name.is_none() { vec![] } else {
185 revwalk.map(|oid|
186 oid
187 .expect("revwalk to produce oids without error")
188 .to_string()
189 ).collect()
190 };
191
192 let latest_commit: Option<&String> = match commit_ids_in_branch.get(0) {
193 None => None,
194 Some(latest_commit) => {
195 // return empty if latest patch is in current chain
196 if commit_ids_in_branch.iter().any(|id|
197 patch_commit_id(&latest_patch_on_branch) == id.to_string()
198 ) { return vec![]; }
199 Some(latest_commit)
200 },
201 };
202
203 // work back thorugh commit chain until we reach a commit in our branch history (tip or ealier for rebase)
204 new_patches_on_branch = vec![latest_patch_on_branch.clone()];
205 loop {
206 let next_parent_patch = new_patches_on_branch.last()
207 .expect("chain to contain at least latest_patch_on_main")
208 .clone();
209 match next_parent_patch.tags.iter().find(|t|tag_is_patch_parent(t)) {
210 None => {
211 // found root patch or error
212 next_parent_patch.tags.iter().find(|t|tag_is_initial_commit(t))
213 // tag_is_initial_commit is false when it should be true. is it always false or just the oposite?
214 .expect(
215 &format!(
216 "reach a patch which doesn't contain a either a tag_is_patch_parent or tag_is_initial_commit{:#?}",
217 &next_parent_patch
218 )
219 );
220 break;
221 },
222 Some(t) => {
223 let next_patch = match patch_events.iter().find(|event|event.id.to_string() == tag_extract_value(t)) {
224 None => {
225 let patch_path = repo_dir_path.join(format!(
226 ".ngit/patches/{}.json",
227 tag_extract_value(
228 next_parent_patch.tags.iter().find(|t|tag_is_commit_parent(t))
229 .expect("patch to always have a commit parent if it has a patch parent")
230 ),
231 ));
232 if patch_path.exists() {
233 load_event(patch_path)
234 .expect("patch json at location that exists loads into event")
235 }
236 else {
237 panic!("cannot find parent patch id {} from patch {:#?}",tag_extract_value(t), next_parent_patch);
238 }
239 },
240 Some(event) => event.clone(),
241 };
242 // if reached current tip - break
243 if latest_commit.is_some() && patch_is_commit(
244 &next_patch,
245 latest_commit.unwrap(),
246 ) { break; }
247 // detect rebase
248 if commit_ids_in_branch.iter().any(|id|
249 patch_commit_id(&next_patch) == id.to_string()
250 ) {
251 panic!("force push detected. This branch has been force pushed since you last pulled. ngit doesnt handle this yet");
252 }
253 // new patch
254 new_patches_on_branch.push(next_patch.clone());
255
256 },
257 }
258 }
259 }
260 // oldest first
261 new_patches_on_branch.reverse();
262 new_patches_on_branch
263}