diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/bin/ngit/sub_commands/checkout.rs | 80 | ||||
| -rw-r--r-- | src/lib/git/mod.rs | 16 |
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>> { |