upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/sub_commands/merge.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/sub_commands/merge.rs')
-rw-r--r--src/sub_commands/merge.rs148
1 files changed, 148 insertions, 0 deletions
diff --git a/src/sub_commands/merge.rs b/src/sub_commands/merge.rs
new file mode 100644
index 0000000..388226b
--- /dev/null
+++ b/src/sub_commands/merge.rs
@@ -0,0 +1,148 @@
1use std::env::current_dir;
2
3use clap::Args;
4
5
6
7
8use crate::{ merge::initialize_merge, repo_config::RepoConfig, utils::{load_event, get_stored_keys, get_or_generate_keys, save_event, create_client}, config::load_config, repos::repo::Repo, fetch_pull_push::fetch_pull_push, funcs::checkout_branch::checkout_branch, branch_refs::BranchRefs};
9
10#[derive(Args)]
11pub struct MergeSubCommand {
12 // TODO: add support merging intop branches
13 // /// branch nevent or hex to pull into a new local branch
14 // #[arg(short, long)]
15 // branch_from: Option<String>,
16 // /// branch nevent or hex to pull into a new local branch
17 // #[arg(short, long)]
18 // branch_to: Option<String>,
19}
20
21pub fn merge(_sub_command_args: &MergeSubCommand) {
22
23 // TODO: add support for merging other branches
24 // loop {
25 // let proposed_to= valid_event_id_from_input(
26 // sub_command_args.branch_from.clone(),
27 // &"branch to merge into (nevent note or hex)".to_string(),
28 // );
29 // // check that branch is in mapping
30 // }
31
32 let repo_dir_path = current_dir().unwrap();
33
34 let git_repo = git2::Repository::open(&repo_dir_path)
35 .expect("git repo not initialized. run ngit init first");
36
37 let repo = Repo::open(&repo_dir_path);
38
39 let mut cfg = load_config();
40
41 let keys = match get_stored_keys(&mut cfg) {
42 None => {
43 get_or_generate_keys(&mut cfg)
44 },
45 Some(k) => k.clone(),
46 };
47
48 let repo_config = RepoConfig::open(&repo_dir_path);
49
50 // check branch isn't main/master.
51 let head = git_repo.head()
52 .expect("head to be returned");
53 if !head.is_branch() {
54 return println!("checkout a branch to merge into main/master");
55 }
56 let branch_name = match head.name() {
57 None => {
58 return println!("checkout a branch to merge into main/master");
59 },
60 Some(head_name) => {
61 let name = head_name.replace("refs/heads/","");
62 if name == "main" || name == "master" {
63 return println!("checkout a branch to merge into main/master");
64 }
65 name
66 },
67 };
68
69 // check user is repo maintainer
70 let branch_refs = BranchRefs::new(vec![], repo_dir_path.clone());
71 if !match branch_refs.is_authorized(None, &keys.public_key()) {
72 None => false,
73 Some(auth) => auth,
74 } {
75 return println!("You are not a repository maintainer so you cannot merge into main/master.");
76 }
77
78 // get latest commit / check the patches are issued
79 let commit_id = head.peel_to_commit()
80 .expect("branch reference to peel back to a commit")
81 .id()
82 .to_string();
83
84
85 let branch_tip_patch_path = repo_dir_path.join(
86 format!(".ngit/patches/{}.json",&commit_id)
87 );
88
89 if !branch_tip_patch_path.exists() {
90 return println!("your branch needs pushing before you can merge!");
91 }
92
93 // TODO: check we have the latest master from relays and that merging isnt a force push (ie.the merged branch must start at the latest commit in master)
94
95 // create merge event referencing the commit id, patch, branch form (TODO other commits not in main, and Pull Requests)
96 let merge_event = initialize_merge(
97 &keys,
98 &repo.id.to_string(),
99 &repo.id.to_string(),
100 &repo_config.branch_id_from_name(&branch_name.to_string())
101 .expect("current branch, that has already been pushed, to be in mapping"),
102 &commit_id,
103 &load_event(&branch_tip_patch_path)
104 .expect("patch event to load from json in path")
105 .id.to_string(),
106 );
107 let merge_path = repo_dir_path.join(
108 format!(".ngit/merges/{}.json",&merge_event.id.to_string())
109 );
110 // save event so fetch_pull_push picks it up when runing get_branch_refs
111 save_event(&merge_path, &merge_event)
112 .expect("merge event to save to path");
113
114 // broadcast
115 let client = create_client(&keys, repo.relays.clone())
116 .expect("create_client returns client");
117
118 // TODO try and apply locally and abort if there are errors before broadcasting
119 match client.send_event(merge_event.clone()) {
120 Ok(_) => (),
121 // TODO: this isn't working - if a relay is specified with a type it will wait 30ish secs and then return successful
122 Err(e) => { println!("error broadcasting event: {}",e); },
123 }
124 // TODO: better error handling here / reporting. potentially warn if taking a while and report on troublesome relays
125
126 // checkout master / main
127 let master_branch = match git_repo.find_branch("master", git2::BranchType::Local) {
128 Ok(branch) => branch,
129 Err(_) => {
130 git_repo.find_branch("main", git2::BranchType::Local)
131 .expect("the main branch to be called main or master")
132 },
133 };
134 checkout_branch(&git_repo, master_branch);
135 // apply commits to master/main
136 // TODO: there should be no need to reach out to the relays agin but using fetch_pull_push without modification is convinient
137 fetch_pull_push(
138 Some(&keys),
139 true,
140 false,
141 None,
142 false,
143 None,
144 Some(client),
145 );
146
147
148}