use lib::testing::{make_git, remove_rebase_lines, GitInitOptions, GitRunOptions};
#[test]
fn test_restack_amended_commit() -> eyre::Result<()> {
let git = make_git()?;
if !git.supports_committer_date_is_author_date()? {
return Ok(());
}
git.init_repo()?;
git.detach_head()?;
git.commit_file("test1", 1)?;
git.commit_file("test2", 2)?;
git.commit_file("test3", 3)?;
git.run(&["checkout", "HEAD^^"])?;
git.run(&["commit", "--amend", "-m", "amend test1.txt"])?;
{
let stdout = git.smartlog()?;
insta::assert_snapshot!(stdout, @r###"
O f777ecc (master) create initial.txt
|\
| @ 024c35c amend test1.txt
|
x 62fc20d (rewritten as 024c35ce) create test1.txt
|
o 96d1c37 create test2.txt
|
o 70deb1e create test3.txt
hint: there is 1 abandoned commit in your commit graph
hint: to fix this, run: git restack
hint: disable this hint by running: git config --global branchless.hint.smartlogFixAbandoned false
"###);
}
{
let (stdout, _stderr) = git.branchless("restack", &["--on-disk"])?;
let stdout = remove_rebase_lines(stdout);
insta::assert_snapshot!(stdout, @r###"
branchless: running command: <git-executable> diff --quiet
Calling Git for on-disk rebase...
branchless: running command: <git-executable> rebase --continue
Finished restacking commits.
No abandoned branches to restack.
O f777ecc (master) create initial.txt
|
@ 024c35c amend test1.txt
|
o 8cd7de6 create test2.txt
|
o b9a0491 create test3.txt
"###);
}
Ok(())
}
#[test]
fn test_restack_consecutive_rewrites() -> eyre::Result<()> {
let git = make_git()?;
if !git.supports_committer_date_is_author_date()? {
return Ok(());
}
git.init_repo()?;
git.detach_head()?;
git.commit_file("test1", 1)?;
git.commit_file("test2", 2)?;
git.commit_file("test3", 3)?;
git.run(&["checkout", "HEAD^^"])?;
git.run(&["commit", "--amend", "-m", "amend test1.txt v1"])?;
git.run(&["commit", "--amend", "-m", "amend test1.txt v2"])?;
{
let (stdout, _stderr) = git.branchless("restack", &["--on-disk"])?;
let stdout = remove_rebase_lines(stdout);
insta::assert_snapshot!(stdout, @r###"
branchless: running command: <git-executable> diff --quiet
Calling Git for on-disk rebase...
branchless: running command: <git-executable> rebase --continue
Finished restacking commits.
No abandoned branches to restack.
O f777ecc (master) create initial.txt
|
@ 662b451 amend test1.txt v2
|
o 8e9bbde create test2.txt
|
o 9dc6dd0 create test3.txt
"###)
}
Ok(())
}
#[test]
fn test_move_abandoned_branch() -> eyre::Result<()> {
let git = make_git()?;
git.init_repo()?;
git.commit_file("test1", 1)?;
git.detach_head()?;
git.run(&["commit", "--amend", "-m", "amend test1.txt v1"])?;
git.run(&["commit", "--amend", "-m", "amend test1.txt v2"])?;
{
let (stdout, _stderr) = git.branchless("restack", &[])?;
let stdout = remove_rebase_lines(stdout);
insta::assert_snapshot!(stdout, @r###"
No abandoned commits to restack.
branchless: processing 1 update: branch master
Finished restacking branches.
:
@ 662b451 (master) amend test1.txt v2
"###);
}
Ok(())
}
#[test]
fn test_amended_initial_commit() -> eyre::Result<()> {
let git = make_git()?;
if !git.supports_committer_date_is_author_date()? {
return Ok(());
}
git.init_repo()?;
git.run(&["config", "branchless.restack.preserveTimestamps", "true"])?;
git.commit_file("test1", 1)?;
git.run(&["checkout", "HEAD^"])?;
git.run(&["commit", "--amend", "-m", "new initial commit"])?;
{
let stdout = git.smartlog()?;
insta::assert_snapshot!(stdout, @r###"
@ 9a9f929 new initial commit
:
O 62fc20d (master) create test1.txt
"###);
}
{
let (stdout, _stderr) =
git.branchless("restack", &["--on-disk", "--force-rewrite", "all()"])?;
let stdout = remove_rebase_lines(stdout);
insta::assert_snapshot!(stdout, @r###"
branchless: running command: <git-executable> diff --quiet
Calling Git for on-disk rebase...
branchless: running command: <git-executable> rebase --continue
Finished restacking commits.
No abandoned branches to restack.
@ 9a9f929 new initial commit
|
O 6d85943 (master) create test1.txt
"###);
}
Ok(())
}
#[test]
fn test_restack_amended_master() -> eyre::Result<()> {
let git = make_git()?;
if !git.supports_committer_date_is_author_date()? {
return Ok(());
}
git.init_repo()?;
git.commit_file("test1", 1)?;
git.commit_file("test2", 2)?;
git.detach_head()?;
git.run(&["checkout", "HEAD^"])?;
git.run(&["commit", "--amend", "-m", "amended test1"])?;
{
let (stdout, _stderr) =
git.branchless("restack", &["--on-disk", "--force-rewrite", "all()"])?;
let stdout = remove_rebase_lines(stdout);
insta::assert_snapshot!(stdout, @r###"
branchless: running command: <git-executable> diff --quiet
Calling Git for on-disk rebase...
branchless: running command: <git-executable> rebase --continue
Finished restacking commits.
No abandoned branches to restack.
:
@ ae94dc2 amended test1
|
O 51452b5 (master) create test2.txt
"###);
}
Ok(())
}
#[test]
fn test_restack_aborts_during_rebase_conflict() -> eyre::Result<()> {
let git = make_git()?;
git.init_repo()?;
git.run(&["branch", "foo"])?;
git.commit_file("test1", 1)?;
git.commit_file("test2", 2)?;
git.run(&["checkout", "HEAD^"])?;
git.write_file_txt("test2", "conflicting test2 contents")?;
git.run(&["add", "."])?;
git.run(&["commit", "--amend", "-m", "amend test1 with test2 conflict"])?;
{
let (stdout, _stderr) = git.branchless_with_options(
"restack",
&["-f", "all()"],
&GitRunOptions {
expected_exit_code: 1,
..Default::default()
},
)?;
insta::assert_snapshot!(stdout, @r###"
Attempting rebase in-memory...
This operation would cause a merge conflict:
- (1 conflicting file) 96d1c37 create test2.txt
To resolve merge conflicts, retry this operation with the --merge option.
"###);
}
{
let (stdout, _stderr) = git.branchless_with_options(
"restack",
&["--force-rewrite", "--merge", "all()"],
&GitRunOptions {
expected_exit_code: 1,
..Default::default()
},
)?;
let stdout = remove_rebase_lines(stdout);
insta::assert_snapshot!(stdout, @r###"
Attempting rebase in-memory...
Failed to merge in-memory, trying again on-disk...
branchless: running command: <git-executable> diff --quiet
Calling Git for on-disk rebase...
branchless: running command: <git-executable> rebase --continue
CONFLICT (add/add): Merge conflict in test2.txt
Error: Could not restack commits (exit code 1).
You can resolve the error and try running `git restack` again.
"###);
}
Ok(())
}
#[test]
fn test_restack_multiple_amended() -> eyre::Result<()> {
let git = make_git()?;
if !git.supports_committer_date_is_author_date()? {
return Ok(());
}
git.init_repo()?;
git.detach_head()?;
git.commit_file("test1", 1)?;
git.commit_file("test2", 2)?;
git.commit_file("test3", 3)?;
git.commit_file("test4", 4)?;
git.run(&["checkout", "HEAD~"])?;
git.run(&["commit", "--amend", "-m", "test3 amended"])?;
git.run(&["checkout", "HEAD~"])?;
git.run(&["commit", "--amend", "-m", "test2 amended"])?;
git.run(&["checkout", "HEAD~"])?;
{
let (stdout, _stderr) = git.branchless("restack", &["--on-disk"])?;
insta::assert_snapshot!(stdout, @r###"
branchless: running command: <git-executable> diff --quiet
Calling Git for on-disk rebase...
branchless: running command: <git-executable> rebase --continue
Finished restacking commits.
No abandoned branches to restack.
O f777ecc (master) create initial.txt
|
@ 62fc20d create test1.txt
|
o 22f3928 test2 amended
|
o 8e06b96 test3 amended
|
o f5644e3 create test4.txt
"###);
}
Ok(())
}
#[test]
fn test_restack_single_of_many_commits() -> eyre::Result<()> {
let git = make_git()?;
if !git.supports_reference_transactions()? {
return Ok(());
}
git.init_repo()?;
let test1_oid = git.commit_file("test1", 1)?;
git.detach_head()?;
let test2_oid = git.commit_file("test2", 2)?;
git.commit_file("test3", 3)?;
git.run(&["checkout", &test1_oid.to_string()])?;
let test4_oid = git.commit_file("test4", 4)?;
git.commit_file("test5", 5)?;
git.run(&["checkout", &test2_oid.to_string()])?;
git.run(&["commit", "--amend", "-m", "updated test2"])?;
git.run(&["checkout", &test4_oid.to_string()])?;
git.run(&["commit", "--amend", "-m", "updated test4"])?;
{
let stdout = git.smartlog()?;
insta::assert_snapshot!(stdout, @r###"
:
O 62fc20d (master) create test1.txt
|\
| @ 3bd716d updated test4
|\
| o 7357d2b updated test2
|\
| x 96d1c37 (rewritten as 7357d2b7) create test2.txt
| |
| o 70deb1e create test3.txt
|
x bf0d52a (rewritten as 3bd716d5) create test4.txt
|
o 848121c create test5.txt
hint: there are 2 abandoned commits in your commit graph
hint: to fix this, run: git restack
hint: disable this hint by running: git config --global branchless.hint.smartlogFixAbandoned false
"###);
}
{
let (stdout, stderr) = git.branchless("restack", &["--on-disk", &test2_oid.to_string()])?;
insta::assert_snapshot!(stderr, @r###"
branchless: processing 1 update: ref HEAD
branchless: processing 1 update: ref HEAD
Executing: git branchless hook-detect-empty-commit 70deb1e28791d8e7dd5a1f0c871a51b91282562f
Executing: git branchless hook-register-extra-post-rewrite-hook
branchless: processing 1 rewritten commit
branchless: creating working copy snapshot
branchless: running command: <git-executable> checkout 3bd716d57489779ab1daf446f80e66e90b56ead7
Previous HEAD position was 944f78d create test3.txt
branchless: processing 1 update: ref HEAD
HEAD is now at 3bd716d updated test4
branchless: processing checkout
:
O 62fc20d (master) create test1.txt
|\
| @ 3bd716d updated test4
|\
| o 7357d2b updated test2
| |
| o 944f78d create test3.txt
|
x bf0d52a (rewritten as 3bd716d5) create test4.txt
|
o 848121c create test5.txt
hint: there is 1 abandoned commit in your commit graph
hint: to fix this, run: git restack
hint: disable this hint by running: git config --global branchless.hint.smartlogFixAbandoned false
Successfully rebased and updated detached HEAD.
"###);
insta::assert_snapshot!(stdout, @r###"
branchless: running command: <git-executable> diff --quiet
Calling Git for on-disk rebase...
branchless: running command: <git-executable> rebase --continue
Finished restacking commits.
No abandoned branches to restack.
:
O 62fc20d (master) create test1.txt
|\
| @ 3bd716d updated test4
|\
| o 7357d2b updated test2
| |
| o 944f78d create test3.txt
|
x bf0d52a (rewritten as 3bd716d5) create test4.txt
|
o 848121c create test5.txt
hint: there is 1 abandoned commit in your commit graph
hint: to fix this, run: git restack
hint: disable this hint by running: git config --global branchless.hint.smartlogFixAbandoned false
"###);
}
Ok(())
}
#[test]
fn test_restack_unobserved_commit() -> eyre::Result<()> {
let git = make_git()?;
git.init_repo_with_options(&GitInitOptions {
run_branchless_init: false,
..Default::default()
})?;
git.commit_file("test1", 1)?;
git.run(&["checkout", "-b", "foo"])?;
git.commit_file("test2", 2)?;
git.detach_head()?;
git.commit_file("test3", 3)?;
git.branchless("init", &[])?;
{
let stdout = git.smartlog()?;
insta::assert_snapshot!(stdout, @r###"
:
O 62fc20d (master) create test1.txt
|
o 96d1c37 (foo) create test2.txt
|
@ 70deb1e create test3.txt
"###);
}
git.run(&["checkout", "foo"])?;
git.run(&["commit", "--amend", "-m", "Updated test2"])?;
{
let stdout = git.smartlog()?;
insta::assert_snapshot!(stdout, @r###"
:
O 62fc20d (master) create test1.txt
|
@ f4229de (> foo) Updated test2
"###);
}
{
let (stdout, _stderr) = git.branchless("restack", &[])?;
insta::assert_snapshot!(stdout, @r###"
No abandoned commits to restack.
No abandoned branches to restack.
:
O 62fc20d (master) create test1.txt
|
@ f4229de (> foo) Updated test2
"###);
}
Ok(())
}
#[test]
fn test_restack_checked_out_branch() -> eyre::Result<()> {
let git = make_git()?;
git.init_repo()?;
git.commit_file("test1", 1)?;
git.commit_file("test2", 2)?;
git.run(&["branch", "foo"])?;
git.run(&["checkout", "HEAD^"])?;
git.run(&["commit", "--amend", "-m", "test1 amended"])?;
git.run(&["checkout", "foo"])?;
{
let (stdout, _stderr) = git.branchless("restack", &["-f", "all()"])?;
insta::assert_snapshot!(stdout, @r###"
Attempting rebase in-memory...
[1/1] Committed as: 59e7581 create test2.txt
branchless: processing 2 updates: branch foo, branch master
branchless: processing 1 rewritten commit
branchless: running command: <git-executable> checkout foo
In-memory rebase succeeded.
Finished restacking commits.
No abandoned branches to restack.
:
@ 59e7581 (> foo, master) create test2.txt
"###);
}
Ok(())
}
#[test]
fn test_restack_non_observed_branch_commit() -> eyre::Result<()> {
let git = make_git()?;
git.init_repo()?;
git.commit_file("test1", 1)?;
let test2_oid = git.commit_file("test2", 2)?;
git.run(&["branch", "foo"])?;
git.run(&["checkout", "HEAD^"])?;
git.clear_event_log()?;
git.run(&["commit", "--amend", "-m", "test1 amended"])?;
git.run(&["checkout", "foo"])?;
{
let (stdout, _stderr) = git.branchless("restack", &["-f", "all()"])?;
insta::assert_snapshot!(stdout, @r###"
Attempting rebase in-memory...
[1/1] Committed as: 59e7581 create test2.txt
branchless: processing 2 updates: branch foo, branch master
branchless: processing 1 rewritten commit
branchless: running command: <git-executable> checkout foo
In-memory rebase succeeded.
Finished restacking commits.
No abandoned branches to restack.
:
@ 59e7581 (> foo, master) create test2.txt
"###);
}
git.run(&["reset", "--hard", &test2_oid.to_string()])?;
{
let (stdout, _stderr) = git.run(&["sl"])?;
insta::assert_snapshot!(stdout, @r###"
O f777ecc create initial.txt
|\
: x 62fc20d (rewritten as 14042005) create test1.txt
: |
: % 96d1c37 (rewritten as 59e75818) (> foo) create test2.txt
:
O 59e7581 (master) create test2.txt
"###);
}
{
let (stdout, _stderr) = git.branchless("restack", &["-f"])?;
insta::assert_snapshot!(stdout, @r###"
No abandoned commits to restack.
branchless: processing 1 update: branch foo
Finished restacking branches.
:
@ 59e7581 (> foo, master) create test2.txt
"###);
}
Ok(())
}