diff --git a/src/core.rs b/src/core.rs index 2e1b623..0f4c0e4 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::convert::TryFrom; use std::fmt::Debug; @@ -17,8 +17,8 @@ use crate::subprocess::{self, get_worktrees, RemoteHead}; use crate::util::ForceSendSync; use crate::{config, BaseSpec, Git}; -#[derive(Default)] pub struct TrimPlan { + pub skipped: HashMap, pub to_delete: HashSet, pub preserved: Vec, } @@ -390,6 +390,29 @@ impl TrimPlan { } } +#[derive(Clone, Eq, PartialEq)] +pub enum SkipSuggestion { + Tracking, + TrackingRemote(String), + NonTracking, + NonUpstream(String), +} + +impl SkipSuggestion { + pub const KIND_TRACKING: i32 = 1; + pub const KIND_NON_TRACKING: i32 = 2; + pub const KIND_NON_UPSTREAM: i32 = 3; + + pub fn kind(&self) -> i32 { + match self { + SkipSuggestion::Tracking => Self::KIND_TRACKING, + SkipSuggestion::TrackingRemote(_) => Self::KIND_TRACKING, + SkipSuggestion::NonTracking => Self::KIND_NON_TRACKING, + SkipSuggestion::NonUpstream(_) => Self::KIND_NON_UPSTREAM, + } + } +} + fn get_protect_pattern<'a, B: Refname>( repo: &Repository, protected_patterns: &[&'a str], diff --git a/src/lib.rs b/src/lib.rs index c0405ac..670898b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,7 @@ use crate::core::{ DirectFetchClassificationRequest, NonTrackingBranchClassificationRequest, NonUpstreamBranchClassificationRequest, TrackingBranchClassificationRequest, }; -pub use crate::core::{ClassifiedBranch, TrimPlan}; +pub use crate::core::{ClassifiedBranch, SkipSuggestion, TrimPlan}; use crate::merge_tracker::MergeTracker; pub use crate::subprocess::{ls_remote_head, remote_update, RemoteHead}; pub use crate::util::ForceSendSync; @@ -87,6 +87,7 @@ pub fn get_trim_plan(git: &Git, param: &PlanParam) -> Result { let merge_tracker = MergeTracker::with_base_upstreams(&git.repo, &git.config, &base_upstreams)?; let mut classifier = Classifier::new(git, &merge_tracker); + let mut skipped = HashMap::new(); info!("Enqueue classification requests"); if param.delete.scan_tracking() { @@ -112,6 +113,21 @@ pub fn get_trim_plan(git: &Git, param: &PlanParam) -> Result { ); } } + } else { + for (local, upstream) in &tracking_branches { + if let Some(upstream) = upstream { + let remote = upstream.to_remote_branch(&git.repo)?.remote; + let suggestion = SkipSuggestion::TrackingRemote(remote); + skipped.insert(local.refname.clone(), suggestion.clone()); + skipped.insert(upstream.refname.clone(), suggestion.clone()); + } else { + skipped.insert(local.refname.clone(), SkipSuggestion::Tracking); + } + } + + for (local, _) in &direct_fetch_branches { + skipped.insert(local.refname.clone(), SkipSuggestion::Tracking); + } } if param.delete.scan_non_tracking_local() { @@ -120,6 +136,10 @@ pub fn get_trim_plan(git: &Git, param: &PlanParam) -> Result { classifier.queue_request(NonTrackingBranchClassificationRequest { base, local }); } } + } else { + for local in &non_tracking_branches { + skipped.insert(local.refname.clone(), SkipSuggestion::NonTracking); + } } for base in &base_upstreams { @@ -130,6 +150,12 @@ pub fn get_trim_plan(git: &Git, param: &PlanParam) -> Result { base, remote: remote_tracking, }); + } else { + let remote = remote_tracking.to_remote_branch(&git.repo)?.remote; + skipped.insert( + remote_tracking.refname.clone(), + SkipSuggestion::NonUpstream(remote), + ); } } } @@ -137,6 +163,7 @@ pub fn get_trim_plan(git: &Git, param: &PlanParam) -> Result { let classifications = classifier.classify()?; let mut result = TrimPlan { + skipped, to_delete: HashSet::new(), preserved: Vec::new(), }; diff --git a/src/main.rs b/src/main.rs index c0a1165..62a7fad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ use git_trim::config::{self, get, Config, ConfigValue}; use git_trim::{ delete_local_branches, delete_remote_branches, get_trim_plan, ls_remote_head, remote_update, ClassifiedBranch, ForceSendSync, Git, LocalBranch, PlanParam, RemoteHead, RemoteTrackingBranch, - TrimPlan, + SkipSuggestion, TrimPlan, }; #[paw::main] @@ -174,8 +174,10 @@ pub fn print_summary(plan: &TrimPlan, repo: &Repository) -> Result<()> { preserved.reason ); } + } else if let Some(suggestion) = plan.skipped.get(refname) { + println!(" {} *{}", branch_name, suggestion.kind()); } else { - println!(" {} [skipped]", branch_name); + println!(" {}", branch_name); } } println!(" remote references:"); @@ -209,8 +211,10 @@ pub fn print_summary(plan: &TrimPlan, repo: &Repository) -> Result<()> { preserved.reason ); } + } else if let Some(suggestion) = plan.skipped.get(refname) { + println!(" {} *{}", shorthand, suggestion.kind()); } else { - println!(" {} [skipped]", shorthand); + println!(" {}", shorthand); } printed_remotes.insert(remote_branch); } @@ -228,6 +232,72 @@ pub fn print_summary(plan: &TrimPlan, repo: &Repository) -> Result<()> { _ => {} } } + + if !plan.skipped.is_empty() { + println!(" Some branches are skipped. Consider following to scan them:"); + let tracking = plan + .skipped + .values() + .any(|suggest| suggest == &SkipSuggestion::Tracking); + let tracking_remotes: Vec<_> = { + let mut tmp = Vec::new(); + for suggest in plan.skipped.values() { + if let SkipSuggestion::TrackingRemote(r) = suggest { + tmp.push(r); + } + } + tmp + }; + if let [single] = tracking_remotes.as_slice() { + println!( + " *{}: Add `--delete 'merged:{}' flag.", + SkipSuggestion::KIND_TRACKING, + single + ); + } else if tracking_remotes.len() > 1 { + println!( + " *{}: Add `--delete 'merged:*' flag.", + SkipSuggestion::KIND_TRACKING, + ); + } else if tracking { + println!( + " *{}: Add `--delete 'merged-local' flag.", + SkipSuggestion::KIND_TRACKING, + ); + } + let non_tracking = plan + .skipped + .values() + .any(|suggest| suggest == &SkipSuggestion::NonTracking); + if non_tracking { + println!( + " *{}: Set an upstream to make it a tracking branch or add `--delete 'local' flag.", + SkipSuggestion::KIND_NON_TRACKING, + ); + } + + let non_upstream_remotes: Vec<_> = { + let mut tmp = Vec::new(); + for suggest in plan.skipped.values() { + if let SkipSuggestion::NonUpstream(r) = suggest { + tmp.push(r); + } + } + tmp + }; + if let [single] = non_upstream_remotes.as_slice() { + println!( + " *{}: Make it upstream of a tracking branch or add `--delete 'remote:{}' flag.", + SkipSuggestion::KIND_NON_UPSTREAM, + single + ); + } else if non_upstream_remotes.len() > 1 { + println!( + " *{}: Make it upstream of a tracking branch or add `--delete 'remote:*' flag.", + SkipSuggestion::KIND_NON_UPSTREAM, + ); + } + } println!(); let mut merged_locals = Vec::new();