upleb.uk

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

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bin/ngit/sub_commands/checkout.rs80
-rw-r--r--src/lib/git/mod.rs16
2 files changed, 79 insertions, 17 deletions
diff --git a/src/bin/ngit/sub_commands/checkout.rs b/src/bin/ngit/sub_commands/checkout.rs
index 47d61ed..6ded778 100644
--- a/src/bin/ngit/sub_commands/checkout.rs
+++ b/src/bin/ngit/sub_commands/checkout.rs
@@ -56,7 +56,10 @@ pub async fn launch(id: &str) -> Result<()> {
56 let proposal = proposals_and_revisions 56 let proposal = proposals_and_revisions
57 .iter() 57 .iter()
58 .find(|e| e.id == event_id) 58 .find(|e| e.id == event_id)
59 .context(format!("proposal with id {} not found in cache", event_id.to_hex()))?; 59 .context(format!(
60 "proposal with id {} not found in cache",
61 event_id.to_hex()
62 ))?;
60 63
61 let cover_letter = event_to_cover_letter(proposal) 64 let cover_letter = event_to_cover_letter(proposal)
62 .context("failed to extract proposal details from proposal root event")?; 65 .context("failed to extract proposal details from proposal root event")?;
@@ -143,12 +146,54 @@ fn checkout_pr(
143 println!("checked out up-to-date proposal branch '{branch_name}'"); 146 println!("checked out up-to-date proposal branch '{branch_name}'");
144 return Ok(()); 147 return Ok(());
145 } 148 }
146 if git_repo.does_commit_exist(&proposal_tip)? { 149
147 git_repo.create_branch_at_commit(&branch_name, &proposal_tip)?; 150 let has_tracking = git_repo.get_upstream_for_branch(&branch_name)?.is_some();
148 git_repo.checkout(&branch_name)?; 151
149 println!("checked out proposal branch and updated tip '{branch_name}'"); 152 if has_tracking {
153 println!(
154 "{}",
155 console::style(format!(
156 "Local branch '{branch_name}' is behind. Run git pull to update."
157 ))
158 .yellow()
159 );
150 return Ok(()); 160 return Ok(());
151 } 161 }
162
163 if git_repo.does_commit_exist(&proposal_tip)? {
164 if git_repo.ancestor_of(&str_to_sha1(&proposal_tip)?, &local_branch_tip)? {
165 git_repo.create_branch_at_commit(&branch_name, &proposal_tip)?;
166 git_repo.checkout(&branch_name)?;
167 println!("checked out proposal branch and updated tip '{branch_name}'");
168 return Ok(());
169 }
170 println!(
171 "{}",
172 console::style(format!(
173 "Branch '{branch_name}' has diverged from proposal tip."
174 ))
175 .yellow()
176 );
177 println!("{}", console::style("To reset to proposal tip:").yellow());
178 println!(
179 "{}",
180 console::style(format!(" git reset --hard {proposal_tip}")).yellow()
181 );
182 println!(
183 "{}",
184 console::style("To rebase local commits onto proposal tip:").yellow()
185 );
186 println!(
187 "{}",
188 console::style(format!(" git rebase {proposal_tip}")).yellow()
189 );
190 bail!("branch diverged from proposal");
191 }
192
193 bail!(
194 "proposal tip {proposal_tip} not found locally and branch has no tracking remote. \n\
195 Try fetching from git servers first."
196 );
152 } 197 }
153 198
154 if let Some(remote_name) = nostr_remote_name { 199 if let Some(remote_name) = nostr_remote_name {
@@ -160,12 +205,7 @@ fn checkout_pr(
160 } 205 }
161 } 206 }
162 207
163 fetch_oid_for_from_servers_for_pr( 208 fetch_oid_for_from_servers_for_pr(&proposal_tip, git_repo, repo_ref, proposal_tip_event)?;
164 &proposal_tip,
165 git_repo,
166 repo_ref,
167 proposal_tip_event,
168 )?;
169 git_repo.create_branch_at_commit(&branch_name, &proposal_tip)?; 209 git_repo.create_branch_at_commit(&branch_name, &proposal_tip)?;
170 git_repo.checkout(&branch_name)?; 210 git_repo.checkout(&branch_name)?;
171 println!("created and checked out proposal branch '{branch_name}'"); 211 println!("created and checked out proposal branch '{branch_name}'");
@@ -207,9 +247,7 @@ fn checkout_patch(
207 } 247 }
208 248
209 if git_repo.has_outstanding_changes()? { 249 if git_repo.has_outstanding_changes()? {
210 bail!( 250 bail!("working directory is not clean. Discard or stash (un)staged changes and try again.");
211 "working directory is not clean. Discard or stash (un)staged changes and try again."
212 );
213 } 251 }
214 252
215 let branch_name = cover_letter.get_branch_name_with_pr_prefix_and_shorthand_id()?; 253 let branch_name = cover_letter.get_branch_name_with_pr_prefix_and_shorthand_id()?;
@@ -224,12 +262,17 @@ fn checkout_patch(
224 let remote_branch = format!("{remote_name}/{branch_name}"); 262 let remote_branch = format!("{remote_name}/{branch_name}");
225 if git_repo.get_tip_of_branch(&remote_branch).is_ok() { 263 if git_repo.get_tip_of_branch(&remote_branch).is_ok() {
226 checkout_remote_branch_with_tracking(git_repo, remote_name, &branch_name)?; 264 checkout_remote_branch_with_tracking(git_repo, remote_name, &branch_name)?;
227 println!("checked out proposal branch '{branch_name}' with tracking to {remote_name}"); 265 println!(
266 "checked out proposal branch '{branch_name}' with tracking to {remote_name}"
267 );
228 return Ok(()); 268 return Ok(());
229 } 269 }
230 } 270 }
231 let _ = git_repo 271 let _ = git_repo
232 .apply_patch_chain(&branch_name, most_recent_proposal_patch_chain_or_pr_or_pr_update.to_vec()) 272 .apply_patch_chain(
273 &branch_name,
274 most_recent_proposal_patch_chain_or_pr_or_pr_update.to_vec(),
275 )
233 .context("failed to apply patch chain")?; 276 .context("failed to apply patch chain")?;
234 println!("checked out proposal as '{branch_name}' branch"); 277 println!("checked out proposal as '{branch_name}' branch");
235 return Ok(()); 278 return Ok(());
@@ -256,7 +299,10 @@ fn checkout_patch(
256 git_repo.create_branch_at_commit(&branch_name, &proposal_base_commit.to_string())?; 299 git_repo.create_branch_at_commit(&branch_name, &proposal_base_commit.to_string())?;
257 git_repo.checkout(&branch_name)?; 300 git_repo.checkout(&branch_name)?;
258 let _ = git_repo 301 let _ = git_repo
259 .apply_patch_chain(&branch_name, most_recent_proposal_patch_chain_or_pr_or_pr_update.to_vec()) 302 .apply_patch_chain(
303 &branch_name,
304 most_recent_proposal_patch_chain_or_pr_or_pr_update.to_vec(),
305 )
260 .context("failed to apply patch chain")?; 306 .context("failed to apply patch chain")?;
261 println!("checked out updated proposal as '{branch_name}' branch"); 307 println!("checked out updated proposal as '{branch_name}' branch");
262 Ok(()) 308 Ok(())
diff --git a/src/lib/git/mod.rs b/src/lib/git/mod.rs
index b9711ae..516d9e2 100644
--- a/src/lib/git/mod.rs
+++ b/src/lib/git/mod.rs
@@ -91,6 +91,7 @@ pub trait RepoActions {
91 ) -> Result<Oid>; 91 ) -> Result<Oid>;
92 fn parse_starting_commits(&self, starting_commits: &str) -> Result<Vec<Sha1Hash>>; 92 fn parse_starting_commits(&self, starting_commits: &str) -> Result<Vec<Sha1Hash>>;
93 fn ancestor_of(&self, decendant: &Sha1Hash, ancestor: &Sha1Hash) -> Result<bool>; 93 fn ancestor_of(&self, decendant: &Sha1Hash, ancestor: &Sha1Hash) -> Result<bool>;
94 fn get_upstream_for_branch(&self, branch_name: &str) -> Result<Option<String>>;
94 fn get_git_config_item(&self, item: &str, global: Option<bool>) -> Result<Option<String>>; 95 fn get_git_config_item(&self, item: &str, global: Option<bool>) -> Result<Option<String>>;
95 fn save_git_config_item(&self, item: &str, value: &str, global: bool) -> Result<()>; 96 fn save_git_config_item(&self, item: &str, value: &str, global: bool) -> Result<()>;
96 fn remove_git_config_item(&self, item: &str, global: bool) -> Result<bool>; 97 fn remove_git_config_item(&self, item: &str, global: bool) -> Result<bool>;
@@ -734,6 +735,21 @@ impl RepoActions for Repo {
734 } 735 }
735 } 736 }
736 737
738 fn get_upstream_for_branch(&self, branch_name: &str) -> Result<Option<String>> {
739 let branch = self
740 .git_repo
741 .find_branch(branch_name, git2::BranchType::Local)
742 .context(format!("failed to find local branch {branch_name}"))?;
743 let upstream = branch.upstream();
744 match upstream {
745 Ok(upstream_branch) => {
746 let name = upstream_branch.name()?.map(|s| s.to_string());
747 Ok(name)
748 }
749 Err(_) => Ok(None),
750 }
751 }
752
737 /// setting global to None will suppliment local config with global items 753 /// setting global to None will suppliment local config with global items
738 /// not in local 754 /// not in local
739 fn get_git_config_item(&self, item: &str, global: Option<bool>) -> Result<Option<String>> { 755 fn get_git_config_item(&self, item: &str, global: Option<bool>) -> Result<Option<String>> {