upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/sub_commands
diff options
context:
space:
mode:
Diffstat (limited to 'src/sub_commands')
-rw-r--r--src/sub_commands/push.rs67
-rw-r--r--src/sub_commands/send.rs14
2 files changed, 64 insertions, 17 deletions
diff --git a/src/sub_commands/push.rs b/src/sub_commands/push.rs
index dd32b2c..75bff2d 100644
--- a/src/sub_commands/push.rs
+++ b/src/sub_commands/push.rs
@@ -11,6 +11,7 @@ use crate::{
11 login, 11 login,
12 repo_ref::{self, RepoRef}, 12 repo_ref::{self, RepoRef},
13 sub_commands::{ 13 sub_commands::{
14 self,
14 list::{ 15 list::{
15 find_commits_for_proposal_root_events, find_proposal_events, get_commit_id_from_patch, 16 find_commits_for_proposal_root_events, find_proposal_events, get_commit_id_from_patch,
16 get_most_recent_patch_with_ancestors, tag_value, 17 get_most_recent_patch_with_ancestors, tag_value,
@@ -20,7 +21,18 @@ use crate::{
20 Cli, 21 Cli,
21}; 22};
22 23
23pub async fn launch(cli_args: &Cli) -> Result<()> { 24#[derive(Debug, clap::Args)]
25pub struct SubCommandArgs {
26 #[arg(long, action)]
27 /// send proposal revision from checked out proposal branch
28 force: bool,
29 #[arg(long, action)]
30 /// dont prompt for cover letter when force pushing
31 no_cover_letter: bool,
32}
33
34#[allow(clippy::too_many_lines)]
35pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> {
24 let git_repo = Repo::discover().context("cannot find a git repository")?; 36 let git_repo = Repo::discover().context("cannot find a git repository")?;
25 37
26 let (main_or_master_branch_name, _) = git_repo 38 let (main_or_master_branch_name, _) = git_repo
@@ -59,26 +71,55 @@ pub async fn launch(cli_args: &Cli) -> Result<()> {
59 ) 71 )
60 .await?; 72 .await?;
61 73
62 // TODO: fix these scenarios:
63 // - local proposal branch is 2 behind and 1 ahead. intructions: ...
64 // - proposal has been rebased. (against commit in main) instructions: ...
65 // - proposal has been rebased. (against commit not in repo) instructions: ..
66
67 let most_recent_proposal_patch_chain = get_most_recent_patch_with_ancestors(commit_events) 74 let most_recent_proposal_patch_chain = get_most_recent_patch_with_ancestors(commit_events)
68 .context("cannot get most recent patch for proposal")?; 75 .context("cannot get most recent patch for proposal")?;
69 76
70 let branch_tip = git_repo.get_tip_of_local_branch(&branch_name)?; 77 let branch_tip = git_repo.get_tip_of_local_branch(&branch_name)?;
71 78
72 let most_recent_patch_commit_id = str_to_sha1( 79 let most_recent_patch_commit_id = str_to_sha1(
73 &get_commit_id_from_patch(&most_recent_proposal_patch_chain[0]) 80 &get_commit_id_from_patch(
74 .context("latest patch event doesnt have a commit tag")?, 81 most_recent_proposal_patch_chain
82 .first()
83 .context("no patches found")?,
84 )
85 .context("latest patch event doesnt have a commit tag")?,
75 ) 86 )
76 .context("latest patch event commit tag isn't a valid SHA1 hash")?; 87 .context("latest patch event commit tag isn't a valid SHA1 hash")?;
77 88
89 let proposal_base_commit_id = str_to_sha1(
90 &tag_value(
91 most_recent_proposal_patch_chain
92 .last()
93 .context("no patches found")?,
94 "parent-commit",
95 )
96 .context("patch is incorrectly formatted")?,
97 )
98 .context("latest patch event parent-commit tag isn't a valid SHA1 hash")?;
99
78 if most_recent_patch_commit_id.eq(&branch_tip) { 100 if most_recent_patch_commit_id.eq(&branch_tip) {
79 bail!("proposal already up-to-date with local branch"); 101 bail!("proposal already up-to-date with local branch");
80 } 102 }
81 103
104 if args.force {
105 println!("preparing to force push proposal revision...");
106 sub_commands::send::launch(
107 cli_args,
108 &sub_commands::send::SubCommandArgs {
109 starting_commit: String::new(),
110 in_reply_to: Some(proposal_root_event.id.to_string()),
111 title: None,
112 description: None,
113 from_branch: None,
114 to_branch: None,
115 no_cover_letter: args.no_cover_letter,
116 },
117 )
118 .await?;
119 println!("force pushed proposal revision");
120 return Ok(());
121 }
122
82 if most_recent_proposal_patch_chain.iter().any(|e| { 123 if most_recent_proposal_patch_chain.iter().any(|e| {
83 let c = tag_value(e, "parent-commit").unwrap_or_default(); 124 let c = tag_value(e, "parent-commit").unwrap_or_default();
84 c.eq(&branch_tip.to_string()) 125 c.eq(&branch_tip.to_string())
@@ -86,9 +127,15 @@ pub async fn launch(cli_args: &Cli) -> Result<()> {
86 bail!("proposal is ahead of local branch"); 127 bail!("proposal is ahead of local branch");
87 } 128 }
88 129
89 let (ahead, behind) = git_repo 130 let Ok((ahead, behind)) = git_repo
90 .get_commits_ahead_behind(&most_recent_patch_commit_id, &branch_tip) 131 .get_commits_ahead_behind(&most_recent_patch_commit_id, &branch_tip)
91 .context("the latest patch in proposal doesnt share an ancestor with your branch.")?; 132 .context("the latest patch in proposal doesnt share an ancestor with your branch.")
133 else {
134 if git_repo.ancestor_of(&proposal_base_commit_id, &branch_tip)? {
135 bail!("local unpublished proposal ammendments. consider force pushing.");
136 }
137 bail!("local unpublished proposal has been rebased. consider force pushing");
138 };
92 139
93 if !behind.is_empty() { 140 if !behind.is_empty() {
94 bail!( 141 bail!(
diff --git a/src/sub_commands/send.rs b/src/sub_commands/send.rs
index 1ccb1f4..ebe23b1 100644
--- a/src/sub_commands/send.rs
+++ b/src/sub_commands/send.rs
@@ -27,26 +27,26 @@ pub struct SubCommandArgs {
27 #[arg(default_value = "")] 27 #[arg(default_value = "")]
28 /// starting commit (commits since in current branch) or commit range, like 28 /// starting commit (commits since in current branch) or commit range, like
29 /// in `git format-patch` 29 /// in `git format-patch`
30 starting_commit: String, 30 pub(crate) starting_commit: String,
31 #[clap(long)] 31 #[clap(long)]
32 /// nevent or event id of an existing proposal for which this is a new 32 /// nevent or event id of an existing proposal for which this is a new
33 /// version 33 /// version
34 in_reply_to: Option<String>, 34 pub(crate) in_reply_to: Option<String>,
35 /// optional cover letter title 35 /// optional cover letter title
36 #[clap(short, long)] 36 #[clap(short, long)]
37 title: Option<String>, 37 pub(crate) title: Option<String>,
38 #[clap(short, long)] 38 #[clap(short, long)]
39 /// optional cover letter description 39 /// optional cover letter description
40 description: Option<String>, 40 pub(crate) description: Option<String>,
41 #[clap(long)] 41 #[clap(long)]
42 /// branch to get changes from (defaults to head) 42 /// branch to get changes from (defaults to head)
43 from_branch: Option<String>, 43 pub(crate) from_branch: Option<String>,
44 #[clap(long)] 44 #[clap(long)]
45 /// destination branch (defaults to main or master) 45 /// destination branch (defaults to main or master)
46 to_branch: Option<String>, 46 pub(crate) to_branch: Option<String>,
47 /// don't ask about a cover letter 47 /// don't ask about a cover letter
48 #[arg(long, action)] 48 #[arg(long, action)]
49 no_cover_letter: bool, 49 pub(crate) no_cover_letter: bool,
50} 50}
51 51
52#[allow(clippy::too_many_lines)] 52#[allow(clippy::too_many_lines)]