Skip to content

Commit

Permalink
git chain rebase --ignore-root
Browse files Browse the repository at this point in the history
  • Loading branch information
dashed committed Mar 21, 2023
1 parent dd619a0 commit 249b081
Show file tree
Hide file tree
Showing 2 changed files with 262 additions and 5 deletions.
33 changes: 30 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1064,7 +1064,7 @@ impl GitChain {
Ok(true)
}

fn rebase(&self, chain_name: &str, step_rebase: bool) -> Result<(), Error> {
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)?;

Expand Down Expand Up @@ -1147,6 +1147,18 @@ impl GitChain {
&chain.branches[index - 1].branch_name
};

if index == 0 && ignore_root {
// Skip the rebase operation for the first branch of the chain.
// Essentially, we do not rebase the first branch against the root branch.
println!();
println!(
"⚠️ Not rebasing branch {} against root branch {}. Skipping.",
&branch.branch_name.bold(),
prev_branch_name.bold()
);
continue;
}

// git rebase --onto <onto> <upstream> <branch>
// git rebase --onto parent_branch fork_point branch.name

Expand Down Expand Up @@ -1262,6 +1274,12 @@ impl GitChain {
return Ok(());
}

if ignore_root {
println!(
"⚠️ Did not rebase chain against root branch: {}",
root_branch.bold()
);
}
if num_of_rebase_operations > 0 {
println!("🎉 Successfully rebased chain {}", chain.name.bold());
} else {
Expand Down Expand Up @@ -1743,7 +1761,8 @@ fn run(arg_matches: ArgMatches) -> Result<(), Error> {

if Chain::chain_exists(&git_chain, &branch.chain_name)? {
let step_rebase = sub_matches.is_present("step");
git_chain.rebase(&branch.chain_name, step_rebase)?;
let ignore_root = sub_matches.is_present("ignore_root");
git_chain.rebase(&branch.chain_name, step_rebase, ignore_root)?;
} else {
eprintln!("Unable to rebase chain.");
eprintln!("Chain does not exist: {}", branch.chain_name.bold());
Expand Down Expand Up @@ -2190,6 +2209,14 @@ where
.value_name("step")
.help("Stop at the first rebase.")
.takes_value(false),
)
.arg(
Arg::with_name("ignore_root")
.short("i")
.long("ignore-root")
.value_name("ignore_root")
.help("Rebase each branch of the chain except for the first branch.")
.takes_value(false),
);

let push_subcommand = SubCommand::with_name("push")
Expand Down Expand Up @@ -2247,7 +2274,7 @@ where

let arg_matches = App::new("git-chain")
.bin_name(executable_name())
.version("0.0.7")
.version("0.0.8")
.author("Alberto Leal <[email protected]>")
.about("Tool for rebasing a chain of local git branches.")
.subcommand(init_subcommand)
Expand Down
234 changes: 232 additions & 2 deletions tests/rebase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ chain_name
.trim_start()
);

// git rebase
// git chain rebase
let args: Vec<&str> = vec!["rebase"];
let output = run_test_bin_expect_ok(&path_to_repo, args);

Expand Down Expand Up @@ -317,7 +317,7 @@ chain_name
.trim_start()
);

// git rebase
// git chain rebase
assert_eq!(&get_current_branch_name(&repo), "some_branch_1");

let args: Vec<&str> = vec!["rebase"];
Expand Down Expand Up @@ -842,3 +842,233 @@ chain_name

teardown_git_repo(repo_name);
}

#[test]
fn rebase_subcommand_ignore_root() {
let repo_name = "rebase_subcommand_ignore_root";
let repo = setup_git_repo(repo_name);
let path_to_repo = generate_path_to_repo(repo_name);

{
// create new file
create_new_file(&path_to_repo, "hello_world.txt", "Hello, world!");

// add first commit to master
first_commit_all(&repo, "first commit");
};

assert_eq!(&get_current_branch_name(&repo), "master");

// create and checkout new branch named some_branch_1
{
let branch_name = "some_branch_1";
create_branch(&repo, branch_name);
checkout_branch(&repo, branch_name);
};

{
assert_eq!(&get_current_branch_name(&repo), "some_branch_1");

// create new file
create_new_file(&path_to_repo, "file_1.txt", "contents 1");

// add commit to branch some_branch_1
commit_all(&repo, "message");
};

// create and checkout new branch named some_branch_2
{
let branch_name = "some_branch_2";
create_branch(&repo, branch_name);
checkout_branch(&repo, branch_name);
};

{
assert_eq!(&get_current_branch_name(&repo), "some_branch_2");

// create new file
create_new_file(&path_to_repo, "file_2.txt", "contents 2");

// add commit to branch some_branch_2
commit_all(&repo, "message");
};

// create and checkout new branch named some_branch_3
{
let branch_name = "some_branch_3";
create_branch(&repo, branch_name);
checkout_branch(&repo, branch_name);
};

{
assert_eq!(&get_current_branch_name(&repo), "some_branch_3");

// create new file
create_new_file(&path_to_repo, "file_3.txt", "contents 3");

// add commit to branch some_branch_3
commit_all(&repo, "message");
};

// create and checkout new branch named some_branch_2.5
{
checkout_branch(&repo, "some_branch_2");
let branch_name = "some_branch_2.5";
create_branch(&repo, branch_name);
checkout_branch(&repo, branch_name);
};

{
assert_eq!(&get_current_branch_name(&repo), "some_branch_2.5");

// create new file
create_new_file(&path_to_repo, "file_2.5.txt", "contents 2.5");

// add commit to branch some_branch_2.5
commit_all(&repo, "message");
};

// create and checkout new branch named some_branch_1.5
{
checkout_branch(&repo, "some_branch_1");
let branch_name = "some_branch_1.5";
create_branch(&repo, branch_name);
checkout_branch(&repo, branch_name);
};

{
assert_eq!(&get_current_branch_name(&repo), "some_branch_1.5");

// create new file
create_new_file(&path_to_repo, "file_1.5.txt", "contents 1.5");

// add commit to branch some_branch_1.5
commit_all(&repo, "message");
};

// create and checkout new branch named some_branch_0
{
checkout_branch(&repo, "master");
let branch_name = "some_branch_0";
create_branch(&repo, branch_name);
checkout_branch(&repo, branch_name);
};

{
assert_eq!(&get_current_branch_name(&repo), "some_branch_0");

// create new file
create_new_file(&path_to_repo, "file_0.txt", "contents 0");

// add commit to branch some_branch_0
commit_all(&repo, "message");
};

// checkout master branch and create a new file
{
checkout_branch(&repo, "master");
assert_eq!(&get_current_branch_name(&repo), "master");

// create new file
create_new_file(&path_to_repo, "canada.txt", "canada");

// add commit to branch master
commit_all(&repo, "message");
};

checkout_branch(&repo, "some_branch_0");
assert_eq!(&get_current_branch_name(&repo), "some_branch_0");

// run git chain setup
let args: Vec<&str> = vec![
"setup",
"chain_name",
"master",
"some_branch_0",
"some_branch_1",
"some_branch_1.5",
"some_branch_2",
"some_branch_2.5",
"some_branch_3",
];
let output = run_test_bin_expect_ok(&path_to_repo, args);

assert_eq!(
String::from_utf8_lossy(&output.stdout),
r#"
🔗 Succesfully set up chain: chain_name
chain_name
some_branch_3 ⦁ 1 ahead ⦁ 1 behind
some_branch_2.5 ⦁ 1 ahead
some_branch_2 ⦁ 1 ahead ⦁ 1 behind
some_branch_1.5 ⦁ 1 ahead
some_branch_1 ⦁ 1 ahead ⦁ 1 behind
➜ some_branch_0 ⦁ 1 ahead ⦁ 1 behind
master (root branch)
"#
.trim_start()
);

// git chain rebase --ignore-root
let args: Vec<&str> = vec!["rebase", "--ignore-root"];
let output = run_test_bin_for_rebase(&path_to_repo, args);

assert!(String::from_utf8_lossy(&output.stdout)
.contains("⚠️ Not rebasing branch some_branch_0 against root branch master. Skipping."));
assert!(
String::from_utf8_lossy(&output.stdout).contains("Switching back to branch: some_branch_0")
);
assert!(String::from_utf8_lossy(&output.stdout)
.contains("⚠️ Did not rebase chain against root branch: master"));
assert!(String::from_utf8_lossy(&output.stdout)
.contains("🎉 Successfully rebased chain chain_name"));

let actual = console::strip_ansi_codes(&String::from_utf8_lossy(&output.stderr))
.trim()
.replace("\r", "\n");
assert!(actual.contains("Successfully rebased and updated refs/heads/some_branch_1."));
assert!(actual.contains("Successfully rebased and updated refs/heads/some_branch_1.5."));
assert!(actual.contains("Successfully rebased and updated refs/heads/some_branch_2."));
assert!(actual.contains("Successfully rebased and updated refs/heads/some_branch_2.5."));

assert!(actual.contains("Successfully rebased and updated refs/heads/some_branch_3."));

// git chain
let args: Vec<&str> = vec![];
let output = run_test_bin_expect_ok(&path_to_repo, args);

assert_eq!(
String::from_utf8_lossy(&output.stdout),
r#"
On branch: some_branch_0
chain_name
some_branch_3 ⦁ 1 ahead
some_branch_2.5 ⦁ 1 ahead
some_branch_2 ⦁ 1 ahead
some_branch_1.5 ⦁ 1 ahead
some_branch_1 ⦁ 1 ahead
➜ some_branch_0 ⦁ 1 ahead ⦁ 1 behind
master (root branch)
"#
.trim_start()
);

// git chain rebase --ignore-root
let args: Vec<&str> = vec!["rebase", "--ignore-root"];
let output = run_test_bin_expect_ok(&path_to_repo, args);

assert!(String::from_utf8_lossy(&output.stdout)
.contains("⚠️ Not rebasing branch some_branch_0 against root branch master. Skipping."));
assert!(
String::from_utf8_lossy(&output.stdout).contains("Switching back to branch: some_branch_0")
);
assert!(String::from_utf8_lossy(&output.stdout)
.contains("⚠️ Did not rebase chain against root branch: master"));
assert!(
String::from_utf8_lossy(&output.stdout).contains("Chain chain_name is already up-to-date.")
);

teardown_git_repo(repo_name);
}

0 comments on commit 249b081

Please sign in to comment.