diff --git a/src/bridge.rs b/src/bridge.rs index c43a3d0..7bb7fd3 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -60,46 +60,18 @@ fn save_git_map(path: &std::path::Path, map: &GitMap) -> Result<()> { Ok(()) } -fn make_cred_callback() --> impl FnMut(&str, Option<&str>, git2::CredentialType) -> std::result::Result -{ - let mut attempts = 0u32; - move |_url, username, allowed| { - attempts += 1; - if attempts > 4 { - return Err(git2::Error::from_str("authentication failed")); - } - - if allowed.contains(git2::CredentialType::SSH_KEY) { - let user = username.unwrap_or("git"); - - if attempts <= 1 - && let Ok(cred) = git2::Cred::ssh_key_from_agent(user) - { - return Ok(cred); - } - - let home = std::env::var("HOME").unwrap_or_default(); - let ssh_dir = std::path::Path::new(&home).join(".ssh"); - for key_name in &["id_ed25519", "id_rsa", "id_ecdsa"] { - let key_path = ssh_dir.join(key_name); - if key_path.exists() { - let pub_path = ssh_dir.join(format!("{key_name}.pub")); - let pub_key = pub_path.exists().then_some(pub_path.as_path()); - if let Ok(cred) = git2::Cred::ssh_key(user, pub_key, &key_path, None) { - return Ok(cred); - } - } - } - - Err(git2::Error::from_str("no SSH key found")) - } else if allowed.contains(git2::CredentialType::USER_PASS_PLAINTEXT) { - Err(git2::Error::from_str( - "interactive authentication not supported", - )) - } else { - git2::Cred::default() - } +fn cred_callback( + _url: &str, + username: Option<&str>, + allowed: git2::CredentialType, +) -> std::result::Result { + if allowed.contains(git2::CredentialType::SSH_KEY) { + let user = username.unwrap_or("git"); + git2::Cred::ssh_key_from_agent(user) + } else if allowed.contains(git2::CredentialType::USER_PASS_PLAINTEXT) { + git2::Cred::userpass_plaintext("", "") + } else { + git2::Cred::default() } } @@ -107,7 +79,6 @@ impl GitBridge { /// Open (or initialise) the shadow git repository and load the mapping cache. pub fn open(repo: &Repository) -> Result { let git_dir = shadow_git_dir(repo); - debug!("opening git bridge at {}", git_dir.display()); let git_repo = if git_dir.exists() { git2::Repository::open_bare(&git_dir)? } else { @@ -115,7 +86,6 @@ impl GitBridge { }; let map_path = git_map_path(repo); let map = load_git_map(&map_path)?; - debug!("loaded git map with {} entries", map.arc_to_git.len()); Ok(Self { git_repo, map, @@ -132,15 +102,7 @@ impl GitBridge { /// /// Returns the git OID for the newly created (or cached) git commit. pub fn arc_to_git(&mut self, arc_repo: &Repository, arc_id: &CommitId) -> Result { - debug!( - "converting arc commit {} to git", - &arc_id.0[..12.min(arc_id.0.len())] - ); if let Some(hex) = self.map.arc_to_git.get(&arc_id.0) { - debug!( - "arc commit {} already mapped to git", - &arc_id.0[..12.min(arc_id.0.len())] - ); let oid = git2::Oid::from_str(hex)?; return Ok(oid); } @@ -169,11 +131,6 @@ impl GitBridge { .git_repo .commit(None, &sig, &sig, &c.message, &git_tree, &parent_refs)?; - debug!( - "created git commit {} for arc {}", - oid, - &arc_id.0[..12.min(arc_id.0.len())] - ); self.map .arc_to_git .insert(arc_id.0.clone(), oid.to_string()); @@ -189,15 +146,7 @@ impl GitBridge { /// Returns the arc `CommitId` for the newly created (or cached) commit. pub fn git_to_arc(&mut self, arc_repo: &Repository, git_oid: git2::Oid) -> Result { let oid_hex = git_oid.to_string(); - debug!( - "converting git commit {} to arc", - &oid_hex[..12.min(oid_hex.len())] - ); if let Some(arc_id) = self.map.git_to_arc.get(&oid_hex) { - debug!( - "git commit {} already mapped to arc", - &oid_hex[..12.min(oid_hex.len())] - ); return Ok(CommitId(arc_id.clone())); } @@ -259,11 +208,6 @@ impl GitBridge { }; store::write_commit_object(arc_repo, &obj)?; - debug!( - "created arc commit {} for git {}", - &commit_id.0[..12.min(commit_id.0.len())], - &oid_hex[..12.min(oid_hex.len())] - ); self.map .arc_to_git .insert(commit_id.0.clone(), oid_hex.clone()); @@ -381,7 +325,6 @@ impl GitBridge { /// /// Converts all reachable commits, updates shadow refs, and pushes. pub fn push(arc_repo: &Repository, remote_name: &str) -> Result { - debug!("pushing to remote '{}'", remote_name); let remotes = remote::load(arc_repo)?; let entry = remotes .remotes @@ -454,7 +397,7 @@ pub fn push(arc_repo: &Repository, remote_name: &str) -> Result { let mut git_remote = bridge.git_repo.remote_anonymous(url)?; let mut opts = git2::PushOptions::new(); let mut callbacks = git2::RemoteCallbacks::new(); - callbacks.credentials(make_cred_callback()); + callbacks.credentials(cred_callback); opts.remote_callbacks(callbacks); git_remote.push(&spec_strs, Some(&mut opts))?; } @@ -462,7 +405,6 @@ pub fn push(arc_repo: &Repository, remote_name: &str) -> Result { bridge.save_map()?; let count = ref_specs.len(); - debug!("pushed {} ref(s)", count); Ok(format!("pushed {count} ref(s) to {remote_name}")) } @@ -471,7 +413,6 @@ pub fn push(arc_repo: &Repository, remote_name: &str) -> Result { /// Fetches, then imports reachable commits for each remote branch /// and updates local bookmarks that can be fast-forwarded. pub fn pull(arc_repo: &Repository, remote_name: &str) -> Result { - debug!("pulling from remote '{}'", remote_name); let remotes = remote::load(arc_repo)?; let entry = remotes .remotes @@ -485,7 +426,7 @@ pub fn pull(arc_repo: &Repository, remote_name: &str) -> Result { let mut git_remote = bridge.git_repo.remote_anonymous(url)?; let mut opts = git2::FetchOptions::new(); let mut callbacks = git2::RemoteCallbacks::new(); - callbacks.credentials(make_cred_callback()); + callbacks.credentials(cred_callback); opts.remote_callbacks(callbacks); git_remote.fetch::<&str>(&[], Some(&mut opts), None)?; @@ -531,7 +472,6 @@ pub fn pull(arc_repo: &Repository, remote_name: &str) -> Result { } result }; - debug!("fetched {} new ref(s)", refs.len()); for (_refname, oid) in &refs { if bridge.map.git_to_arc.contains_key(&oid.to_string()) { @@ -626,7 +566,6 @@ pub fn pull(arc_repo: &Repository, remote_name: &str) -> Result { /// Creates the target directory, initialises an arc repo, imports all /// git history, and sets up the worktree at the specified branch. pub fn clone(url: &str, path: &str, branch: &str) -> Result { - debug!("cloning from '{}'", url); let target = std::path::Path::new(path); let created_dir = !target.exists(); if created_dir { @@ -651,7 +590,7 @@ fn clone_inner(url: &str, target: &std::path::Path, branch: &str) -> Result Result Result Result { - debug!("migrating git repository at {}", path.display()); let git_repo = git2::Repository::discover(path).map_err(|_| ArcError::NotAGitRepo)?; let workdir = git_repo.workdir().ok_or(ArcError::NotAGitRepo)?; @@ -802,7 +735,6 @@ pub fn migrate(path: &std::path::Path) -> Result { } result }; - debug!("found {} git ref(s) to import", refs.len()); let mut bridge = GitBridge { git_repo, @@ -867,12 +799,6 @@ pub fn migrate(path: &std::path::Path) -> Result { bridge.save_map()?; - debug!( - "migration complete: {} commit(s), {} bookmark(s), {} tag(s)", - imported, - bookmarks.len(), - tags.len() - ); Ok(format!( "migrated {imported} commit(s), {} bookmark(s), {} tag(s)", bookmarks.len(), @@ -885,7 +811,6 @@ pub fn migrate(path: &std::path::Path) -> Result { /// Without `--push`, this ensures the shadow git repo mirrors arc state. /// With `--push`, it also pushes all refs to the default remote. pub fn sync(arc_repo: &Repository, do_push: bool) -> Result { - debug!("syncing refs to shadow git"); let mut bridge = GitBridge::open(arc_repo)?; let mut synced = 0usize; @@ -930,7 +855,6 @@ pub fn sync(arc_repo: &Repository, do_push: bool) -> Result { } bridge.save_map()?; - debug!("synced {} ref(s)", synced); if do_push { let config = crate::config::load_effective(arc_repo); @@ -945,11 +869,6 @@ pub fn sync(arc_repo: &Repository, do_push: bool) -> Result { /// Check whether `ancestor` is an ancestor of `descendant` by walking /// the first-parent chain from `descendant`. fn is_ancestor(repo: &Repository, ancestor: &CommitId, descendant: &CommitId) -> bool { - debug!( - "checking if {} is ancestor of {}", - &ancestor.0[..12.min(ancestor.0.len())], - &descendant.0[..12.min(descendant.0.len())] - ); let mut current = descendant.clone(); loop { if current == *ancestor { diff --git a/src/cli.rs b/src/cli.rs index 7aab0a4..7bd470b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,6 +1,5 @@ use std::collections::BTreeMap; use std::path::Path; -use std::sync::atomic::{AtomicBool, Ordering}; use clap::{Parser, Subcommand}; @@ -17,21 +16,9 @@ use crate::stash; use crate::tracking; use crate::ui; -static VERBOSE: AtomicBool = AtomicBool::new(false); - -/// Returns whether verbose/debug output is enabled. -#[allow(dead_code)] -pub fn verbose() -> bool { - VERBOSE.load(Ordering::Relaxed) -} - #[derive(Parser)] #[command(name = "arc", about = "A delta-based version control system", version)] pub struct Cli { - /// Enable verbose/debug output - #[arg(short, long, global = true)] - pub verbose: bool, - #[command(subcommand)] pub command: Command, } @@ -367,11 +354,8 @@ pub fn parse() -> Cli { } pub fn dispatch(cli: Cli) { - VERBOSE.store(cli.verbose, Ordering::Relaxed); - debug!("dispatching command"); match cli.command { Command::Init { path } => { - debug!("command: init (path: {:?})", path); let target = path.as_deref().unwrap_or("."); let target_path = Path::new(target); if !target_path.exists() @@ -397,7 +381,6 @@ pub fn dispatch(cli: Cli) { } } Command::Commit { message } => { - debug!("command: commit (message: {})", message); let repo = open_repo_or_exit(); match tracking::commit(&repo, &message) { Ok(id) => { @@ -413,7 +396,6 @@ pub fn dispatch(cli: Cli) { } } Command::Log { range } => { - debug!("command: log (range: {:?})", range); let repo = open_repo_or_exit(); match inspect::log(&repo, range.as_deref()) { Ok(output) => print!("{output}"), @@ -424,7 +406,6 @@ pub fn dispatch(cli: Cli) { } } Command::Status => { - debug!("command: status"); let repo = open_repo_or_exit(); match tracking::status(&repo) { Ok((report, _)) => { @@ -437,7 +418,6 @@ pub fn dispatch(cli: Cli) { } } Command::Diff { range: _range } => { - debug!("command: diff (range: {:?})", _range); let repo = open_repo_or_exit(); match run_diff(&repo) { Ok(output) => { @@ -454,7 +434,6 @@ pub fn dispatch(cli: Cli) { } } Command::Switch { target } => { - debug!("command: switch (target: {})", target); let repo = open_repo_or_exit(); match refs::switch(&repo, &target) { Ok(msg) => println!("{msg}"), @@ -465,7 +444,6 @@ pub fn dispatch(cli: Cli) { } } Command::Merge { target } => { - debug!("command: merge (target: {})", target); let repo = open_repo_or_exit(); match modify::merge_branch(&repo, &target) { Ok(id) => println!( @@ -483,7 +461,6 @@ pub fn dispatch(cli: Cli) { } } Command::Show { target } => { - debug!("command: show (target: {})", target); let repo = open_repo_or_exit(); match inspect::show(&repo, &target) { Ok(output) => print!("{output}"), @@ -494,7 +471,6 @@ pub fn dispatch(cli: Cli) { } } Command::History { file, range } => { - debug!("command: history (file: {}, range: {:?})", file, range); let repo = open_repo_or_exit(); match inspect::history(&repo, &file, range.as_deref()) { Ok(output) => print!("{output}"), @@ -505,7 +481,6 @@ pub fn dispatch(cli: Cli) { } } Command::Revert { target } => { - debug!("command: revert (target: {})", target); let repo = open_repo_or_exit(); match modify::revert(&repo, &target) { Ok(id) => println!( @@ -519,7 +494,6 @@ pub fn dispatch(cli: Cli) { } } Command::Reset { files } => { - debug!("command: reset (files: {:?})", files); let repo = open_repo_or_exit(); match modify::reset(&repo, &files) { Ok(msg) => println!("{msg}"), @@ -530,7 +504,6 @@ pub fn dispatch(cli: Cli) { } } Command::Push { remote } => { - debug!("command: push (remote: {:?})", remote); let repo = open_repo_or_exit(); let config = config::load_effective(&repo); let r = remote.as_deref().unwrap_or(&config.default_remote); @@ -543,7 +516,6 @@ pub fn dispatch(cli: Cli) { } } Command::Pull { remote } => { - debug!("command: pull (remote: {:?})", remote); let repo = open_repo_or_exit(); let config = config::load_effective(&repo); let r = remote.as_deref().unwrap_or(&config.default_remote); @@ -556,10 +528,6 @@ pub fn dispatch(cli: Cli) { } } Command::Clone { url, path, branch } => { - debug!( - "command: clone (url: {}, path: {:?}, branch: {:?})", - url, path, branch - ); let p = path.as_deref().unwrap_or("."); let b = branch.as_deref().unwrap_or("main"); match bridge::clone(&url, p, b) { @@ -570,18 +538,14 @@ pub fn dispatch(cli: Cli) { } } } - Command::Migrate => { - debug!("command: migrate"); - match bridge::migrate(Path::new(".")) { - Ok(msg) => println!("{msg}"), - Err(e) => { - eprintln!("{}", ui::error(&e.to_string())); - std::process::exit(1); - } + Command::Migrate => match bridge::migrate(Path::new(".")) { + Ok(msg) => println!("{msg}"), + Err(e) => { + eprintln!("{}", ui::error(&e.to_string())); + std::process::exit(1); } - } + }, Command::Mark { command } => { - debug!("command: mark"); let repo = open_repo_or_exit(); match command { MarkCommand::Add { name, commit } => { @@ -636,7 +600,6 @@ pub fn dispatch(cli: Cli) { } } Command::Tag { command } => { - debug!("command: tag"); let repo = open_repo_or_exit(); match command { TagCommand::Add { name, commit } => { @@ -675,7 +638,6 @@ pub fn dispatch(cli: Cli) { } } Command::Stash { command } => { - debug!("command: stash"); let repo = open_repo_or_exit(); match command { StashCommand::Create { name } => match stash::create(&repo, &name) { @@ -723,7 +685,6 @@ pub fn dispatch(cli: Cli) { } } Command::Graft { target, onto } => { - debug!("command: graft (target: {}, onto: {})", target, onto); let repo = open_repo_or_exit(); match modify::graft(&repo, &target, &onto) { Ok(ids) => { @@ -740,70 +701,66 @@ pub fn dispatch(cli: Cli) { } } } - Command::Config { command } => { - debug!("command: config"); - match command { - ConfigCommand::Set { global, key, value } => { - let repo = if global { - None - } else { - Some(open_repo_or_exit()) - }; - match config::config_set(repo.as_ref(), global, &key, &value) { - Ok(()) => println!("{}", ui::success(&format!("{key} = {value}"))), - Err(e) => { - eprintln!("{}", ui::error(&e.to_string())); - std::process::exit(1); - } - } - } - ConfigCommand::Get { global, key } => { - let repo = if global { - None - } else { - Repository::discover(Path::new(".")).ok() - }; - match config::config_get(repo.as_ref(), global, &key) { - Ok(Some(value)) => println!("{value}"), - Ok(None) => println!("{}", ui::info(&format!("{key} is not set"))), - Err(e) => { - eprintln!("{}", ui::error(&e.to_string())); - std::process::exit(1); - } - } - } - ConfigCommand::Show { global } => { - let repo = if global { - None - } else { - Repository::discover(Path::new(".")).ok() - }; - match config::config_show(repo.as_ref(), global) { - Ok(yaml) => print!("{yaml}"), - Err(e) => { - eprintln!("{}", ui::error(&e.to_string())); - std::process::exit(1); - } - } - } - ConfigCommand::Unset { global, key } => { - let repo = if global { - None - } else { - Some(open_repo_or_exit()) - }; - match config::config_unset(repo.as_ref(), global, &key) { - Ok(()) => println!("{}", ui::success(&format!("unset {key}"))), - Err(e) => { - eprintln!("{}", ui::error(&e.to_string())); - std::process::exit(1); - } + Command::Config { command } => match command { + ConfigCommand::Set { global, key, value } => { + let repo = if global { + None + } else { + Some(open_repo_or_exit()) + }; + match config::config_set(repo.as_ref(), global, &key, &value) { + Ok(()) => println!("{}", ui::success(&format!("{key} = {value}"))), + Err(e) => { + eprintln!("{}", ui::error(&e.to_string())); + std::process::exit(1); } } } - } + ConfigCommand::Get { global, key } => { + let repo = if global { + None + } else { + Repository::discover(Path::new(".")).ok() + }; + match config::config_get(repo.as_ref(), global, &key) { + Ok(Some(value)) => println!("{value}"), + Ok(None) => println!("{}", ui::info(&format!("{key} is not set"))), + Err(e) => { + eprintln!("{}", ui::error(&e.to_string())); + std::process::exit(1); + } + } + } + ConfigCommand::Show { global } => { + let repo = if global { + None + } else { + Repository::discover(Path::new(".")).ok() + }; + match config::config_show(repo.as_ref(), global) { + Ok(yaml) => print!("{yaml}"), + Err(e) => { + eprintln!("{}", ui::error(&e.to_string())); + std::process::exit(1); + } + } + } + ConfigCommand::Unset { global, key } => { + let repo = if global { + None + } else { + Some(open_repo_or_exit()) + }; + match config::config_unset(repo.as_ref(), global, &key) { + Ok(()) => println!("{}", ui::success(&format!("unset {key}"))), + Err(e) => { + eprintln!("{}", ui::error(&e.to_string())); + std::process::exit(1); + } + } + } + }, Command::Sync { push } => { - debug!("command: sync (push: {})", push); let repo = open_repo_or_exit(); match bridge::sync(&repo, push) { Ok(msg) => println!("{msg}"), @@ -814,7 +771,6 @@ pub fn dispatch(cli: Cli) { } } Command::Remote { command } => { - debug!("command: remote"); let repo = open_repo_or_exit(); match command { RemoteCommand::Add { name, url } => match remote::add(&repo, &name, &url) { @@ -853,7 +809,6 @@ pub fn dispatch(cli: Cli) { } fn open_repo_or_exit() -> Repository { - debug!("discovering repository from current directory"); match Repository::discover(Path::new(".")) { Ok(repo) => repo, Err(e) => { @@ -864,7 +819,6 @@ fn open_repo_or_exit() -> Repository { } fn run_diff(repo: &Repository) -> crate::error::Result { - debug!("computing diff"); let ignore = IgnoreRules::load(&repo.workdir); let head_commit = tracking::resolve_head_commit(repo)?; diff --git a/src/config.rs b/src/config.rs index 8cd1e92..ac4915f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -46,19 +46,11 @@ pub struct EffectiveConfig { impl Config { pub fn load_local(repo: &Repository) -> Result> { - debug!( - "loading local config from {}", - repo.local_config_path().display() - ); let path = repo.local_config_path(); Self::load_from(&path) } pub fn load_global() -> Result> { - debug!( - "loading global config from {}", - Self::global_config_path().display() - ); let path = Self::global_config_path(); Self::load_from(&path) } @@ -138,7 +130,6 @@ impl Config { } pub fn load_effective(repo: &crate::repo::Repository) -> EffectiveConfig { - debug!("loading effective config"); let local = Config::load_local(repo).ok().flatten(); let global = Config::load_global().ok().flatten(); Config::effective(local, global) @@ -239,7 +230,6 @@ fn unset_field(config: &mut Config, section: &str, field: &str) -> Result<()> { /// Set a configuration value in the local or global config file. pub fn config_set(repo: Option<&Repository>, global: bool, key: &str, value: &str) -> Result<()> { - debug!("setting config {key} = {value} (global: {global})"); let (section, field) = parse_key(key)?; if global { let path = Config::global_config_path(); @@ -257,7 +247,6 @@ pub fn config_set(repo: Option<&Repository>, global: bool, key: &str, value: &st /// Get a configuration value, resolving local-first then global. pub fn config_get(repo: Option<&Repository>, global: bool, key: &str) -> Result> { - debug!("getting config {key} (global: {global})"); let (section, field) = parse_key(key)?; if global { let config = Config::load_global()?.unwrap_or_default(); @@ -293,7 +282,6 @@ pub fn config_show(repo: Option<&Repository>, global: bool) -> Result { /// Remove a configuration key from the local or global config file. pub fn config_unset(repo: Option<&Repository>, global: bool, key: &str) -> Result<()> { - debug!("unsetting config {key} (global: {global})"); let (section, field) = parse_key(key)?; if global { let path = Config::global_config_path(); diff --git a/src/diff.rs b/src/diff.rs index 9e49db5..6c28e8f 100644 --- a/src/diff.rs +++ b/src/diff.rs @@ -3,7 +3,6 @@ use crate::tracking::FileTree; use crate::ui; pub fn render_diff(committed: &FileTree, changes: &[FileChange]) -> String { - debug!("rendering diff for {} change(s)", changes.len()); let mut output = String::new(); for change in changes { diff --git a/src/ignore.rs b/src/ignore.rs index 29d1a86..0e86a7e 100644 --- a/src/ignore.rs +++ b/src/ignore.rs @@ -12,7 +12,6 @@ pub struct IgnorePattern { impl IgnoreRules { pub fn load(workdir: &Path) -> Self { - debug!("loading ignore rules from {}", workdir.display()); let arcignore = workdir.join(".arcignore"); let ignore = workdir.join(".ignore"); @@ -24,14 +23,12 @@ impl IgnoreRules { None }; - let rules = match path { + match path { Some(p) => Self::parse_file(&p), None => Self { patterns: Vec::new(), }, - }; - debug!("loaded {} ignore pattern(s)", rules.patterns.len()); - rules + } } fn parse_file(path: &Path) -> Self { diff --git a/src/inspect.rs b/src/inspect.rs index fce5779..f298806 100644 --- a/src/inspect.rs +++ b/src/inspect.rs @@ -13,7 +13,6 @@ use crate::tracking; use crate::ui; pub fn log(repo: &Repository, range: Option<&str>) -> Result { - debug!("showing log (range: {:?})", range); let resolved = resolve::parse_and_resolve_range(repo, range)?; let chain = &resolved.chain[resolved.start_idx..]; @@ -52,7 +51,6 @@ pub fn log(repo: &Repository, range: Option<&str>) -> Result { } pub fn show(repo: &Repository, target: &str) -> Result { - debug!("showing commit '{}'", target); let commit_id = resolve::resolve_target(repo, target)?; let obj = store::read_commit_object(repo, &commit_id)?; let c = &obj.commit; @@ -119,7 +117,6 @@ pub fn show(repo: &Repository, target: &str) -> Result { } pub fn history(repo: &Repository, file: &str, range: Option<&str>) -> Result { - debug!("showing history for file '{}'", file); let resolved = resolve::parse_and_resolve_range(repo, range)?; let chain = &resolved.chain[resolved.start_idx..]; diff --git a/src/main.rs b/src/main.rs index 3ea7e1a..9702d2e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,3 @@ -#[macro_use] -pub mod ui; - pub mod bridge; mod cli; pub mod config; @@ -19,6 +16,7 @@ pub mod signing; pub mod stash; pub mod store; pub mod tracking; +pub mod ui; fn main() { let cli = cli::parse(); diff --git a/src/merge.rs b/src/merge.rs index f93149f..0ba1e6c 100644 --- a/src/merge.rs +++ b/src/merge.rs @@ -15,11 +15,6 @@ pub fn three_way_merge(base: &FileTree, ours: &FileTree, theirs: &FileTree) -> M .chain(theirs.keys()) .collect(); - debug!( - "performing three-way merge across {} path(s)", - all_paths.len() - ); - let mut tree = FileTree::new(); let mut conflicts = Vec::new(); @@ -76,7 +71,6 @@ pub fn three_way_merge(base: &FileTree, ours: &FileTree, theirs: &FileTree) -> M } } - debug!("merge result: {} conflict(s)", conflicts.len()); MergeOutcome { tree, conflicts } } diff --git a/src/modify.rs b/src/modify.rs index 2857dd1..d063f73 100644 --- a/src/modify.rs +++ b/src/modify.rs @@ -12,7 +12,6 @@ use crate::store::{self, CommitObject}; use crate::tracking::{self, FileTree}; pub fn reset(repo: &Repository, files: &[String]) -> Result { - debug!("resetting worktree"); let head_commit = tracking::resolve_head_commit(repo)?; let ignore = IgnoreRules::load(&repo.workdir); @@ -79,7 +78,6 @@ pub fn reset(repo: &Repository, files: &[String]) -> Result { refs::remove_empty_dirs(&repo.workdir)?; - debug!("reset {} file(s)", reset_count); if reset_count == 0 { Ok("no matching files to reset".to_string()) } else { @@ -88,12 +86,10 @@ pub fn reset(repo: &Repository, files: &[String]) -> Result { } pub fn revert(repo: &Repository, target: &str) -> Result { - debug!("reverting target '{}'", target); require_clean_worktree(repo)?; let head_id = tracking::resolve_head_commit(repo)?.ok_or(ArcError::NoCommitsYet)?; let commits = resolve_commit_or_range(repo, target)?; - debug!("processing revert for {} commit(s)", commits.len()); let mut current_tree = tracking::materialize_committed_tree(repo, &head_id)?; @@ -127,7 +123,6 @@ pub fn revert(repo: &Repository, target: &str) -> Result { } pub fn merge_branch(repo: &Repository, target: &str) -> Result { - debug!("merging target '{}'", target); require_clean_worktree(repo)?; let ours_id = tracking::resolve_head_commit(repo)?.ok_or(ArcError::NoCommitsYet)?; @@ -138,7 +133,6 @@ pub fn merge_branch(repo: &Repository, target: &str) -> Result { } let base_id = find_merge_base(repo, &ours_id, &theirs_id)?; - debug!("merge base: {:?}", base_id); let base_tree = match &base_id { Some(id) => tracking::materialize_committed_tree(repo, id)?, @@ -155,13 +149,11 @@ pub fn merge_branch(repo: &Repository, target: &str) -> Result { return Err(ArcError::MergeConflicts(outcome.conflicts)); } - debug!("merge completed"); let message = format!("merge {target}"); commit_tree(repo, &message, vec![ours_id, theirs_id], &outcome.tree) } pub fn graft(repo: &Repository, target: &str, onto: &str) -> Result> { - debug!("grafting target '{}' onto '{}'", target, onto); require_clean_worktree(repo)?; let source_commits = resolve_commit_or_range(repo, target)?; @@ -202,7 +194,6 @@ pub fn graft(repo: &Repository, target: &str, onto: &str) -> Result Result Result<()> { - debug!("checking worktree is clean"); let (report, _) = tracking::status(repo)?; if !report.is_clean() { return Err(ArcError::DirtyWorktree); @@ -256,11 +246,6 @@ fn find_merge_base( ours: &CommitId, theirs: &CommitId, ) -> Result> { - debug!( - "searching for merge base between {} and {}", - &ours.0[..12.min(ours.0.len())], - &theirs.0[..12.min(theirs.0.len())] - ); let mut ours_ancestors = HashSet::new(); collect_ancestors(repo, ours, &mut ours_ancestors)?; ours_ancestors.insert(ours.0.clone()); diff --git a/src/refs.rs b/src/refs.rs index 72deaea..bcb274f 100644 --- a/src/refs.rs +++ b/src/refs.rs @@ -49,13 +49,11 @@ fn short_id(id: &CommitId) -> &str { pub fn mark_add(repo: &Repository, name: &str, commit: Option<&str>) -> Result { crate::repo::validate_ref_name(name)?; let id = resolve_commit_or_head(repo, commit)?; - debug!("adding bookmark '{}' at {id}", name); write_ref_target(&repo.bookmarks_dir().join(name), &id)?; Ok(id) } pub fn mark_rm(repo: &Repository, name: &str) -> Result<()> { - debug!("removing bookmark '{}'", name); crate::repo::validate_ref_name(name)?; let path = repo.bookmarks_dir().join(name); if !path.exists() { @@ -71,7 +69,6 @@ pub fn mark_rm(repo: &Repository, name: &str) -> Result<()> { } pub fn mark_list(repo: &Repository) -> Result { - debug!("listing bookmarks"); let active = active_bookmark(repo)?; let dir = repo.bookmarks_dir(); let mut entries: Vec = Vec::new(); @@ -107,7 +104,6 @@ pub fn mark_list(repo: &Repository) -> Result { } pub fn mark_rename(repo: &Repository, name: &str, new_name: &str) -> Result<()> { - debug!("renaming bookmark '{}' to '{}'", name, new_name); crate::repo::validate_ref_name(name)?; crate::repo::validate_ref_name(new_name)?; let old_path = repo.bookmarks_dir().join(name); @@ -146,13 +142,11 @@ pub fn tag_add(repo: &Repository, name: &str, commit: Option<&str>) -> Result Result<()> { - debug!("removing tag '{}'", name); crate::repo::validate_ref_name(name)?; let path = repo.tags_dir().join(name); if !path.exists() { @@ -163,7 +157,6 @@ pub fn tag_rm(repo: &Repository, name: &str) -> Result<()> { } pub fn tag_list(repo: &Repository) -> Result { - debug!("listing tags"); let dir = repo.tags_dir(); let mut names: Vec = Vec::new(); for entry in fs::read_dir(&dir)? { @@ -192,7 +185,6 @@ pub fn tag_list(repo: &Repository) -> Result { } pub fn switch(repo: &Repository, target: &str) -> Result { - debug!("switching to target '{}'", target); let ignore = IgnoreRules::load(&repo.workdir); let head_commit = tracking::resolve_head_commit(repo)?; @@ -238,7 +230,6 @@ pub fn switch(repo: &Repository, target: &str) -> Result { Head::Unborn { .. } => unreachable!(), }; - debug!("target resolved, writing new worktree"); clean_tracked_files(repo, &committed)?; let new_tree = tracking::materialize_committed_tree(repo, &target_commit)?; @@ -250,7 +241,6 @@ pub fn switch(repo: &Repository, target: &str) -> Result { } pub fn clean_tracked_files(repo: &Repository, tree: &tracking::FileTree) -> Result<()> { - debug!("cleaning {} tracked file(s) from worktree", tree.len()); for path in tree.keys() { crate::repo::validate_repo_path(path)?; let abs = repo.workdir.join(path); @@ -288,7 +278,6 @@ pub fn remove_empty_dirs(dir: &std::path::Path) -> Result<()> { } pub fn write_tree(repo: &Repository, tree: &tracking::FileTree) -> Result<()> { - debug!("writing {} file(s) to worktree", tree.len()); for (path, bytes) in tree { crate::repo::validate_repo_path(path)?; let abs = repo.workdir.join(path); @@ -305,7 +294,6 @@ pub fn update_refs_after_commit( head: &Head, commit_id: &CommitId, ) -> Result<()> { - debug!("updating refs after commit {commit_id}"); let ref_target = RefTarget { commit: Some(commit_id.clone()), }; diff --git a/src/remote.rs b/src/remote.rs index 5e36879..d906e79 100644 --- a/src/remote.rs +++ b/src/remote.rs @@ -26,7 +26,6 @@ fn remotes_path(repo: &Repository) -> std::path::PathBuf { /// /// Returns an empty `RemotesFile` if the file does not yet exist. pub fn load(repo: &Repository) -> Result { - debug!("loading remotes from {}", remotes_path(repo).display()); let path = remotes_path(repo); if !path.exists() { return Ok(RemotesFile { @@ -50,7 +49,6 @@ pub fn save(repo: &Repository, file: &RemotesFile) -> Result<()> { /// The name is validated as a ref name. Returns an error if the remote /// already exists. pub fn add(repo: &Repository, name: &str, url: &str) -> Result<()> { - debug!("adding remote '{}' at {}", name, url); crate::repo::validate_ref_name(name)?; let mut file = load(repo)?; if file.remotes.contains_key(name) { @@ -69,7 +67,6 @@ pub fn add(repo: &Repository, name: &str, url: &str) -> Result<()> { /// /// Returns an error if the remote does not exist. pub fn rm(repo: &Repository, name: &str) -> Result<()> { - debug!("removing remote '{}'", name); let mut file = load(repo)?; if file.remotes.remove(name).is_none() { return Err(ArcError::RemoteNotFound(name.to_string())); @@ -82,7 +79,6 @@ pub fn rm(repo: &Repository, name: &str) -> Result<()> { /// Each line has the form ` \t\n`. /// Returns an empty string if no remotes are configured. pub fn list(repo: &Repository) -> Result { - debug!("listing remotes"); let file = load(repo)?; let mut out = String::new(); for (name, entry) in &file.remotes { diff --git a/src/repo.rs b/src/repo.rs index 958a6f8..e6cd460 100644 --- a/src/repo.rs +++ b/src/repo.rs @@ -14,7 +14,6 @@ pub struct Repository { impl Repository { pub fn init(path: &Path) -> Result { - debug!("initializing repository at {}", path.display()); let workdir = path.canonicalize().map_err(|_| { ArcError::invalid_path(format!("cannot resolve path: {}", path.display())) })?; @@ -44,12 +43,10 @@ impl Repository { let ref_yaml = serde_yaml::to_string(&ref_target)?; fs::write(repo.bookmarks_dir().join("main"), ref_yaml)?; - debug!("created .arc directory structure"); Ok(repo) } pub fn open(path: &Path) -> Result { - debug!("opening repository at {}", path.display()); let workdir = path.canonicalize().map_err(|_| { ArcError::invalid_path(format!("cannot resolve path: {}", path.display())) })?; @@ -63,14 +60,12 @@ impl Repository { } pub fn discover(from: &Path) -> Result { - debug!("discovering repository from {}", from.display()); let mut current = from.canonicalize().map_err(|_| { ArcError::invalid_path(format!("cannot resolve path: {}", from.display())) })?; loop { if current.join(ARC_DIR).is_dir() { - debug!("found repository at {}", current.display()); return Self::open(¤t); } if !current.pop() { @@ -104,14 +99,12 @@ impl Repository { } pub fn load_head(&self) -> Result { - debug!("loading HEAD from {}", self.head_path().display()); let contents = fs::read_to_string(self.head_path())?; let head: Head = serde_yaml::from_str(&contents)?; Ok(head) } pub fn save_head(&self, head: &Head) -> Result<()> { - debug!("saving HEAD: {:?}", head); let yaml = serde_yaml::to_string(head)?; fs::write(self.head_path(), yaml)?; Ok(()) diff --git a/src/resolve.rs b/src/resolve.rs index 3806a87..a30849a 100644 --- a/src/resolve.rs +++ b/src/resolve.rs @@ -7,11 +7,8 @@ use crate::store::CommitObject; use crate::tracking; pub fn resolve_target(repo: &Repository, target: &str) -> Result { - debug!("resolving target '{}'", target); if target == "HEAD" { - let id = tracking::resolve_head_commit(repo)?.ok_or(ArcError::NoCommitsYet)?; - debug!("resolved '{}' to {}", target, &id.0); - return Ok(id); + return tracking::resolve_head_commit(repo)?.ok_or(ArcError::NoCommitsYet); } if crate::repo::validate_ref_name(target).is_ok() { @@ -20,7 +17,6 @@ pub fn resolve_target(repo: &Repository, target: &str) -> Result { let contents = fs::read_to_string(&bookmark_path)?; let ref_target: RefTarget = serde_yaml::from_str(&contents)?; if let Some(id) = ref_target.commit { - debug!("resolved '{}' to {}", target, &id.0); return Ok(id); } } @@ -30,19 +26,15 @@ pub fn resolve_target(repo: &Repository, target: &str) -> Result { let contents = fs::read_to_string(&tag_path)?; let ref_target: RefTarget = serde_yaml::from_str(&contents)?; if let Some(id) = ref_target.commit { - debug!("resolved '{}' to {}", target, &id.0); return Ok(id); } } } - let id = resolve_commit_prefix(repo, target)?; - debug!("resolved '{}' to {}", target, &id.0); - Ok(id) + resolve_commit_prefix(repo, target) } fn resolve_commit_prefix(repo: &Repository, prefix: &str) -> Result { - debug!("searching commits with prefix '{}'", prefix); let commits_dir = repo.commits_dir(); let entries = match fs::read_dir(&commits_dir) { Ok(e) => e, @@ -59,7 +51,6 @@ fn resolve_commit_prefix(repo: &Repository, prefix: &str) -> Result { } } - debug!("found {} match(es) for prefix '{}'", matches.len(), prefix); match matches.len() { 0 => Err(ArcError::UnknownRevision(prefix.to_string())), 1 => { @@ -79,7 +70,6 @@ pub struct ResolvedRange { } pub fn parse_and_resolve_range(repo: &Repository, spec: Option<&str>) -> Result { - debug!("parsing range: {:?}", spec); let (start, end) = parse_range_spec(spec)?; let end_target = end.as_deref().unwrap_or("HEAD"); diff --git a/src/signing.rs b/src/signing.rs index f6a0679..dfd5885 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -10,7 +10,6 @@ const NAMESPACE: &str = "arc"; /// /// Returns the signature as a PEM-encoded string. pub fn sign(key_path: &str, data: &[u8]) -> Result { - debug!("signing with key {key_path}"); let expanded = expand_path(key_path); let path = Path::new(&expanded); @@ -28,7 +27,6 @@ pub fn sign(key_path: &str, data: &[u8]) -> Result { /// /// The public key is extracted from the signature itself for verification. pub fn verify(signature_pem: &str, data: &[u8]) -> Result { - debug!("verifying signature"); let sig: SshSig = signature_pem .parse() .map_err(|e| ArcError::SigningError(format!("invalid signature: {e}")))?; diff --git a/src/stash.rs b/src/stash.rs index 30da72e..b9addce 100644 --- a/src/stash.rs +++ b/src/stash.rs @@ -93,7 +93,6 @@ fn save_stash_file(repo: &Repository, name: &str, file: &StashFile) -> Result<() /// Create a new named stash and set it as active. pub fn create(repo: &Repository, name: &str) -> Result<()> { - debug!("creating stash '{}'", name); repo::validate_ref_name(name)?; fs::create_dir_all(stash_named_dir(repo))?; @@ -115,7 +114,6 @@ pub fn create(repo: &Repository, name: &str) -> Result<()> { /// Switch the active stash to an existing named stash. pub fn use_stash(repo: &Repository, name: &str) -> Result<()> { - debug!("switching to stash '{}'", name); repo::validate_ref_name(name)?; let path = stash_file_path(repo, name); @@ -133,7 +131,6 @@ pub fn use_stash(repo: &Repository, name: &str) -> Result<()> { /// Push current dirty changes onto the active stash and reset the worktree. pub fn push(repo: &Repository) -> Result { - debug!("pushing changes to active stash"); let state = load_state(repo)?; let name = state.active.ok_or(ArcError::NoActiveStash)?; repo::validate_ref_name(&name)?; @@ -213,13 +210,11 @@ pub fn push(repo: &Repository) -> Result { refs::remove_empty_dirs(&repo.workdir)?; let n = changes.len(); - debug!("pushed {} change(s) to stash '{}'", n, name); Ok(format!("pushed {n} change(s) to stash '{name}'")) } /// Pop the most recent entry from the active stash and apply it to the worktree. pub fn pop(repo: &Repository) -> Result { - debug!("popping from active stash"); let state = load_state(repo)?; let name = state.active.ok_or(ArcError::NoActiveStash)?; repo::validate_ref_name(&name)?; @@ -238,11 +233,6 @@ pub fn pop(repo: &Repository) -> Result { .entries .pop() .ok_or_else(|| ArcError::StashEmpty(name.clone()))?; - debug!( - "popping {} change(s) from stash '{}'", - entry.changes.len(), - name - ); let head_commit = tracking::resolve_head_commit(repo)?; if entry.base != head_commit { @@ -281,7 +271,6 @@ pub fn pop(repo: &Repository) -> Result { /// Remove a named stash. If it was active, deactivate it. pub fn rm(repo: &Repository, name: &str) -> Result<()> { - debug!("removing stash '{}'", name); repo::validate_ref_name(name)?; let path = stash_file_path(repo, name); @@ -302,7 +291,6 @@ pub fn rm(repo: &Repository, name: &str) -> Result<()> { /// List all named stashes, marking the active one. pub fn list(repo: &Repository) -> Result { - debug!("listing stashes"); let state = load_state(repo)?; let active = state.active.as_deref(); diff --git a/src/store.rs b/src/store.rs index 36a82e7..ab133d1 100644 --- a/src/store.rs +++ b/src/store.rs @@ -20,7 +20,6 @@ pub fn commit_object_path(repo: &Repository, id: &CommitId) -> PathBuf { } pub fn write_commit_object(repo: &Repository, obj: &CommitObject) -> Result<()> { - debug!("writing commit object {}", obj.commit.id.0); let msgpack = rmp_serde::to_vec(obj)?; let compressed = zstd::stream::encode_all(Cursor::new(&msgpack), 3).map_err(std::io::Error::other)?; @@ -35,7 +34,6 @@ pub fn write_commit_object(repo: &Repository, obj: &CommitObject) -> Result<()> } pub fn read_commit_object(repo: &Repository, id: &CommitId) -> Result { - debug!("reading commit object {}", id.0); let path = commit_object_path(repo, id); let compressed = fs::read(&path)?; let mut decoder = @@ -70,7 +68,6 @@ struct CommitForHash<'a> { } pub fn compute_delta_id(base: &Option, changes: &[FileChange]) -> Result { - debug!("computing delta id (base: {:?})", base); let hashable = DeltaForHash { base, changes }; let bytes = rmp_serde::to_vec(&hashable) .map_err(|e| crate::error::ArcError::HashError(e.to_string()))?; @@ -84,7 +81,6 @@ pub fn compute_commit_id( author: &Option, timestamp: i64, ) -> Result { - debug!("computing commit id for message: {}", message); let hashable = CommitForHash { parents, delta, diff --git a/src/tracking.rs b/src/tracking.rs index 1aa64c9..eb1c066 100644 --- a/src/tracking.rs +++ b/src/tracking.rs @@ -14,10 +14,8 @@ use crate::ui; pub type FileTree = BTreeMap>; pub fn scan_worktree(repo: &Repository, ignore: &IgnoreRules) -> Result { - debug!("scanning worktree at {}", repo.workdir.display()); let mut tree = BTreeMap::new(); scan_dir(&repo.workdir, &repo.workdir, ignore, &mut tree)?; - debug!("found {} file(s) in worktree", tree.len()); Ok(tree) } @@ -63,18 +61,15 @@ fn to_rel_string(root: &Path, abs: &Path) -> String { } pub fn materialize_committed_tree(repo: &Repository, head: &CommitId) -> Result { - debug!("materializing tree at commit {}", head.0); let history = load_linear_history(repo, head)?; let mut tree = BTreeMap::new(); for obj in &history { apply_delta(&mut tree, &obj.delta); } - debug!("materialized tree with {} file(s)", tree.len()); Ok(tree) } pub fn load_linear_history(repo: &Repository, head: &CommitId) -> Result> { - debug!("loading history from {}", head.0); let mut chain = Vec::new(); let mut current = head.clone(); @@ -89,7 +84,6 @@ pub fn load_linear_history(repo: &Repository, head: &CommitId) -> Result Vec Result> { - debug!("resolving HEAD commit"); let head = repo.load_head()?; match head { - Head::Unborn { .. } => { - debug!("HEAD is unborn"); - Ok(None) - } - Head::Attached { commit, .. } => { - debug!("HEAD at {}", commit.0); - Ok(Some(commit)) - } - Head::Detached { commit } => { - debug!("HEAD at {}", commit.0); - Ok(Some(commit)) - } + Head::Unborn { .. } => Ok(None), + Head::Attached { commit, .. } => Ok(Some(commit)), + Head::Detached { commit } => Ok(Some(commit)), } } pub fn commit(repo: &Repository, message: &str) -> Result { - debug!("committing with message: {}", message); let ignore = IgnoreRules::load(&repo.workdir); let head = repo.load_head()?; let head_commit = resolve_head_commit(repo)?; @@ -188,7 +170,6 @@ pub fn commit(repo: &Repository, message: &str) -> Result { let worktree = scan_worktree(repo, &ignore)?; let changes = detect_changes(&committed, &worktree); - debug!("found {} change(s) to commit", changes.len()); if changes.is_empty() { return Err(ArcError::NothingToCommit); @@ -250,7 +231,6 @@ pub fn commit(repo: &Repository, message: &str) -> Result { crate::refs::update_refs_after_commit(repo, &head, &commit_id)?; - debug!("created commit {}", commit_id.0); Ok(commit_id) } @@ -337,7 +317,6 @@ impl fmt::Display for StatusReport { } pub fn status(repo: &Repository) -> Result<(StatusReport, Vec)> { - debug!("computing status"); let ignore = IgnoreRules::load(&repo.workdir); let head_commit = resolve_head_commit(repo)?; diff --git a/src/ui.rs b/src/ui.rs index 1507fab..c80e47a 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,14 +1,5 @@ use colored::Colorize; -#[macro_export] -macro_rules! debug { - ($($arg:tt)*) => { - if $crate::cli::verbose() { - eprintln!("{} {}", $crate::ui::SYM_ARROW, format!($($arg)*)); - } - }; -} - pub const SYM_ARROW: &str = "▸"; pub const SYM_CHECK: &str = "✓"; pub const SYM_CROSS: &str = "✗";