From 33ec5a37563ff8db23454e0c80b0d6ab18457b41 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Thu, 6 Mar 2025 16:58:11 -0900 Subject: [PATCH 1/2] add `merge` command --- src/main.rs | 152 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 116 insertions(+), 36 deletions(-) diff --git a/src/main.rs b/src/main.rs index 76688c6..f2391b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1064,46 +1064,15 @@ impl GitChain { } fn rebase(&self, chain_name: &str, step_rebase: bool, ignore_root: bool) -> Result<(), Error> { - // invariant: chain_name chain exists - let chain = Chain::get_chain(self, chain_name)?; - - // ensure root branch exists - if !self.git_branch_exists(&chain.root_branch)? { - eprintln!("Root branch does not exist: {}", chain.root_branch.bold()); - process::exit(1); - } - - // ensure each branch exists - for branch in &chain.branches { - if !self.git_local_branch_exists(&branch.branch_name)? { - eprintln!("Branch does not exist: {}", branch.branch_name.bold()); - process::exit(1); - } - } - - // ensure repository is in a clean state - match self.repo.state() { - RepositoryState::Clean => { - // go ahead to rebase. - } - _ => { - eprintln!("šŸ›‘ Repository needs to be in a clean state before rebasing."); - process::exit(1); + switch (self.preliminary_checks(chain_name)) { + Ok(_) => {} + Err(e) => { + return Err(Error::from_str(&format!("šŸ›‘ Unable to rebase chain: {}", chain_name))); } } - if self.dirty_working_directory()? { - eprintln!( - "šŸ›‘ Unable to rebase branches for the chain: {}", - chain.name.bold() - ); - eprintln!("You have uncommitted changes in your working directory."); - eprintln!("Please commit or stash them."); - process::exit(1); - } - + let chain = Chain::get_chain(self, chain_name)?; let orig_branch = self.get_current_branch_name()?; - let root_branch = chain.root_branch; // List of common ancestors between each branch and its parent branch. @@ -1502,6 +1471,93 @@ impl GitChain { Ok(common_point == ancestor_object.id()) } + + fn preliminary_checks(&self, chain_name: &str) -> Result<(), Error> { + if !Chain::chain_exists(self, chain_name)? { + return Err(Error::from_str(&format!("Chain {} does not exist", chain_name))); + } + + // invariant: chain_name chain exists + let chain = Chain::get_chain(self, chain_name)?; + + // ensure root branch exists + if !self.git_branch_exists(&chain.root_branch)? { + return Err(Error::from_str(&format!("Root branch does not exist: {}", chain.root_branch.bold()))); + } + + // ensure each branch exists + for branch in &chain.branches { + if !self.git_local_branch_exists(&branch.branch_name)? { + return Err(Error::from_str(&format!("Branch does not exist: {}", branch.branch_name.bold()))); + } + } + + // ensure repository is in a clean state + match self.repo.state() { + RepositoryState::Clean => { + // safe to proceed + } + _ => { + return Err(Error::from_str(&format!("Repository needs to be in a clean state before merging."))); + } + } + + if self.dirty_working_directory()? { + return Err(Error::from_str(&format!("You have uncommitted changes in your working directory."))); + } + + Ok(()) + } + + fn merge_chain(&self, chain_name: &str) -> Result<(), Error> { + switch (self.preliminary_checks(chain_name)) { + Ok(_) => {} + Err(e) => { + return Err(Error::from_str(&format!("šŸ›‘ Unable to merge chain {}: {}", chain_name, e))); + } + } + + let chain = Chain::get_chain(self, chain_name)?; + let orig_branch = self.get_current_branch_name()?; + + let mut previous_branch = &chain.root_branch; + + for branch in &chain.branches { + self.checkout_branch(&branch.branch_name)?; + + let command = format!("git merge {}", previous_branch); + + let output = Command::new("git") + .arg("merge") + .arg(previous_branch) + .output() + .unwrap_or_else(|_| panic!("Unable to run: {}", &command)); + + println!("\n{}", command); + + if !output.status.success() { + eprintln!("Command returned non-zero exit status: {}", command); + eprintln!("It returned: {}", output.status.code().unwrap()); + io::stdout().write_all(&output.stdout).unwrap(); + io::stderr().write_all(&output.stderr).unwrap(); + process::exit(1); + } + io::stdout().write_all(&output.stdout).unwrap(); + io::stderr().write_all(&output.stderr).unwrap(); + + previous_branch = &branch.branch_name; + } + + let current_branch = self.get_current_branch_name()?; + + if current_branch != orig_branch { + println!("\nSwitching back to branch: {}", orig_branch.bold()); + self.checkout_branch(&orig_branch)?; + } + + println!("\nšŸŽ‰ Successfully merged chain {}", chain.name.bold()); + Ok(()) + } } fn parse_sort_option( @@ -2098,6 +2154,26 @@ fn run(arg_matches: ArgMatches) -> Result<(), Error> { process::exit(1); } } + ("merge", Some(_sub_matches)) => { + // Merge all branches for the current chain. + let branch_name = git_chain.get_current_branch_name()?; + + let branch = match Branch::get_branch_with_chain(&git_chain, &branch_name)? { + BranchSearchResult::NotPartOfAnyChain(_) => { + git_chain.display_branch_not_part_of_chain_error(&branch_name); + process::exit(1); + } + BranchSearchResult::Branch(branch) => branch, + }; + + if Chain::chain_exists(&git_chain, &branch.chain_name)? { + git_chain.merge_chain(&branch.chain_name)?; + } else { + eprintln!("Unable to merge chain."); + eprintln!("Chain does not exist: {}", branch.chain_name.bold()); + process::exit(1); + } + } _ => { git_chain.run_status()?; } @@ -2276,6 +2352,9 @@ where .index(3), ); + let merge_subcommand = SubCommand::with_name("merge") + .about("Merge all branches for the current chain."); + let arg_matches = App::new("git-chain") .bin_name(executable_name()) .version("0.0.9") @@ -2289,6 +2368,7 @@ where .subcommand(prune_subcommand) .subcommand(setup_subcommand) .subcommand(rename_subcommand) + .subcommand(merge_subcommand) .subcommand(SubCommand::with_name("list").about("List all chains.")) .subcommand( SubCommand::with_name("backup").about("Back up all branches of the current chain."), From 6c39bbc963a3ce9df5b452654f0d09ad55836590 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Thu, 6 Mar 2025 17:02:10 -0900 Subject: [PATCH 2/2] fix issues --- src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index f2391b7..f506e00 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1064,10 +1064,10 @@ impl GitChain { } fn rebase(&self, chain_name: &str, step_rebase: bool, ignore_root: bool) -> Result<(), Error> { - switch (self.preliminary_checks(chain_name)) { + match self.preliminary_checks(chain_name) { Ok(_) => {} Err(e) => { - return Err(Error::from_str(&format!("šŸ›‘ Unable to rebase chain: {}", chain_name))); + return Err(Error::from_str(&format!("šŸ›‘ Unable to rebase chain {}: {}", chain_name, e))); } } @@ -1510,7 +1510,7 @@ impl GitChain { } fn merge_chain(&self, chain_name: &str) -> Result<(), Error> { - switch (self.preliminary_checks(chain_name)) { + match self.preliminary_checks(chain_name) { Ok(_) => {} Err(e) => { return Err(Error::from_str(&format!("šŸ›‘ Unable to merge chain {}: {}", chain_name, e)));