use lib::git::GitVersion;
use lib::testing::{
make_git_with_remote_repo, remove_nondeterministic_lines, GitInitOptions, GitRunOptions,
GitWrapperWithRemoteRepo,
};
const MIN_VERSION: GitVersion = GitVersion(2, 36, 0);
fn redact_remotes(output: String) -> String {
output
.lines()
.map(|line| {
if line.contains("To file://") {
"To: file://<remote>\n".to_string()
} else if line.contains("From file://") {
"From: file://<remote>\n".to_string()
} else if line.contains("error: failed to push some refs to 'file://") {
"error: failed to push some refs to 'file://<remote>'\n".to_string()
} else {
format!("{line}\n")
}
})
.collect()
}
#[test]
fn test_submit() -> eyre::Result<()> {
let GitWrapperWithRemoteRepo {
temp_dir: _guard,
original_repo,
cloned_repo,
} = make_git_with_remote_repo()?;
if original_repo.get_version()? < MIN_VERSION {
return Ok(());
}
{
original_repo.init_repo()?;
original_repo.commit_file("test1", 1)?;
original_repo.commit_file("test2", 2)?;
original_repo.clone_repo_into(&cloned_repo, &[])?;
}
cloned_repo.init_repo_with_options(&GitInitOptions {
make_initial_commit: false,
..Default::default()
})?;
cloned_repo.run(&["checkout", "-b", "foo"])?;
cloned_repo.commit_file("test3", 3)?;
cloned_repo.run(&["checkout", "-b", "bar", "master"])?;
cloned_repo.commit_file("test4", 4)?;
cloned_repo.run(&["checkout", "-b", "qux"])?;
cloned_repo.commit_file("test5", 5)?;
{
let (stdout, stderr) = cloned_repo.run(&["submit"])?;
insta::assert_snapshot!(stderr, @"");
insta::assert_snapshot!(stdout, @r###"
Skipped 2 commits (not yet on remote): bar, qux
These commits were skipped because they were not already associated with a remote
repository. To submit them, retry this operation with the --create option.
"###);
}
{
let (stdout, stderr) = cloned_repo.run(&["submit", "bar+qux"])?;
insta::assert_snapshot!(stderr, @"");
insta::assert_snapshot!(stdout, @r###"
Skipped 2 commits (not yet on remote): bar, qux
These commits were skipped because they were not already associated with a remote
repository. To submit them, retry this operation with the --create option.
"###);
}
{
let (stdout, stderr) = cloned_repo.run(&["submit", "bar", "qux"])?;
insta::assert_snapshot!(stderr, @"");
insta::assert_snapshot!(stdout, @r###"
Skipped 2 commits (not yet on remote): bar, qux
These commits were skipped because they were not already associated with a remote
repository. To submit them, retry this operation with the --create option.
"###);
}
{
let (stdout, stderr) = cloned_repo.run(&["submit", "--dry-run"])?;
insta::assert_snapshot!(stderr, @"");
insta::assert_snapshot!(stdout, @r###"
Would skip 2 commits (not yet on remote): bar, qux
These commits would be skipped because they are not already associated with a remote
repository. To submit them, retry this operation with the --create option.
"###);
}
{
let (stdout, stderr) = cloned_repo.run(&["submit", "--create", "--dry-run"])?;
insta::assert_snapshot!(stderr, @"");
insta::assert_snapshot!(stdout, @r###"
Would submit 2 commits: bar, qux
"###);
}
{
let (stdout, stderr) = cloned_repo.run(&["submit", "--create"])?;
let stderr = redact_remotes(stderr);
insta::assert_snapshot!(stderr, @r###"
branchless: processing 1 update: branch bar
branchless: processing 1 update: branch qux
To: file://<remote>
* [new branch] bar -> bar
* [new branch] qux -> qux
branchless: processing 1 update: remote branch origin/bar
branchless: processing 1 update: remote branch origin/qux
"###);
insta::assert_snapshot!(stdout, @r###"
branchless: running command: <git-executable> push --set-upstream origin bar qux
branch 'bar' set up to track 'origin/bar'.
branch 'qux' set up to track 'origin/qux'.
Submitted 2 commits: bar, qux
"###);
}
{
let (stdout, stderr) = original_repo.run(&["branch", "-a"])?;
insta::assert_snapshot!(stderr, @"");
insta::assert_snapshot!(stdout, @r###"
bar
* master
qux
"###);
}
cloned_repo.run(&["commit", "--amend", "-m", "updated message"])?;
{
let (stdout, stderr) = cloned_repo.run(&["submit"])?;
let stderr = redact_remotes(stderr);
insta::assert_snapshot!(stderr, @r###"
From: file://<remote>
* branch bar -> FETCH_HEAD
* branch qux -> FETCH_HEAD
branchless: processing 1 update: branch qux
To: file://<remote>
+ 20230db...bae8307 qux -> qux (forced update)
branchless: processing 1 update: remote branch origin/qux
"###);
insta::assert_snapshot!(stdout, @r###"
branchless: running command: <git-executable> fetch origin refs/heads/bar refs/heads/qux
branchless: running command: <git-executable> push --force-with-lease origin qux
Updated 1 commit: qux
Skipped 1 commit (already up-to-date): bar
"###);
}
{
let (stdout, stderr) = cloned_repo.run(&["submit", "--create"])?;
let stderr = redact_remotes(stderr);
insta::assert_snapshot!(stderr, @r###"
From: file://<remote>
* branch bar -> FETCH_HEAD
* branch qux -> FETCH_HEAD
"###);
insta::assert_snapshot!(stdout, @r###"
branchless: running command: <git-executable> fetch origin refs/heads/bar refs/heads/qux
Skipped 2 commits (already up-to-date): bar, qux
"###);
}
Ok(())
}
#[test]
fn test_submit_multiple_remotes() -> eyre::Result<()> {
let GitWrapperWithRemoteRepo {
temp_dir: _guard,
original_repo,
cloned_repo,
} = make_git_with_remote_repo()?;
if original_repo.get_version()? < MIN_VERSION {
return Ok(());
}
{
original_repo.init_repo()?;
original_repo.commit_file("test1", 1)?;
original_repo.commit_file("test2", 2)?;
original_repo.clone_repo_into(&cloned_repo, &[])?;
}
cloned_repo.init_repo_with_options(&GitInitOptions {
make_initial_commit: false,
..Default::default()
})?;
cloned_repo.run(&["checkout", "-b", "foo"])?;
cloned_repo.commit_file("test3", 3)?;
cloned_repo.run(&["branch", "--unset-upstream", "master"])?;
cloned_repo.run(&["remote", "add", "other-repo", "file://dummy-file"])?;
{
let (stdout, stderr) = cloned_repo.branchless_with_options(
"submit",
&["--create"],
&GitRunOptions {
expected_exit_code: 1,
..Default::default()
},
)?;
insta::assert_snapshot!(stderr, @"");
insta::assert_snapshot!(stdout, @r###"
No upstream repository was associated with branch master and no value was
specified for `remote.pushDefault`, so cannot push these branches: foo
Configure a value with: git config remote.pushDefault <remote>
These remotes are available: origin, other-repo
"###);
}
Ok(())
}
#[test]
fn test_submit_existing_branch() -> eyre::Result<()> {
let GitWrapperWithRemoteRepo {
temp_dir: _guard,
original_repo,
cloned_repo,
} = make_git_with_remote_repo()?;
if original_repo.get_version()? < MIN_VERSION {
return Ok(());
}
original_repo.init_repo()?;
original_repo.commit_file("test1", 1)?;
original_repo.commit_file("test2", 2)?;
original_repo.clone_repo_into(&cloned_repo, &[])?;
cloned_repo.init_repo_with_options(&GitInitOptions {
make_initial_commit: false,
..Default::default()
})?;
original_repo.run(&["checkout", "-b", "feature"])?;
original_repo.commit_file("test3", 3)?;
cloned_repo.run(&["checkout", "-b", "feature"])?;
cloned_repo.commit_file("test4", 4)?;
{
let (stdout, stderr) = cloned_repo.branchless_with_options(
"submit",
&["--create"],
&GitRunOptions {
expected_exit_code: 1,
..Default::default()
},
)?;
let stderr = redact_remotes(stderr);
let stderr = remove_nondeterministic_lines(stderr);
insta::assert_snapshot!(stderr, @r###"
To: file://<remote>
! [rejected] feature -> feature (fetch first)
error: failed to push some refs to 'file://<remote>'
"###);
insta::assert_snapshot!(stdout, @"branchless: running command: <git-executable> push --set-upstream origin feature
");
}
{
cloned_repo.run(&["fetch"])?;
let (stdout, _stderr) = cloned_repo.run(&["branch", "--all", "--verbose"])?;
insta::assert_snapshot!(stdout, @r###"
* feature f57e36f create test4.txt
master 96d1c37 create test2.txt
remotes/origin/HEAD -> origin/master
remotes/origin/feature 70deb1e create test3.txt
remotes/origin/master 96d1c37 create test2.txt
"###);
}
Ok(())
}
#[test]
fn test_submit_up_to_date_branch() -> eyre::Result<()> {
let GitWrapperWithRemoteRepo {
temp_dir: _guard,
original_repo,
cloned_repo,
} = make_git_with_remote_repo()?;
if original_repo.get_version()? < MIN_VERSION {
return Ok(());
}
{
original_repo.init_repo()?;
original_repo.commit_file("test1", 1)?;
original_repo.commit_file("test2", 2)?;
original_repo.clone_repo_into(&cloned_repo, &[])?;
cloned_repo.init_repo_with_options(&GitInitOptions {
make_initial_commit: false,
..Default::default()
})?;
}
cloned_repo.run(&["checkout", "-b", "feature"])?;
cloned_repo.commit_file("test3", 3)?;
{
let (stdout, stderr) = cloned_repo.run(&["submit", "--create", "feature"])?;
let stderr = redact_remotes(stderr);
insta::assert_snapshot!(stderr, @r###"
branchless: processing 1 update: branch feature
To: file://<remote>
* [new branch] feature -> feature
branchless: processing 1 update: remote branch origin/feature
"###);
insta::assert_snapshot!(stdout, @r###"
branchless: running command: <git-executable> push --set-upstream origin feature
branch 'feature' set up to track 'origin/feature'.
Submitted 1 commit: feature
"###);
}
cloned_repo.detach_head()?;
{
let (stdout, stderr) = cloned_repo.run(&["submit", "feature"])?;
let stderr = redact_remotes(stderr);
insta::assert_snapshot!(stderr, @r###"
From: file://<remote>
* branch feature -> FETCH_HEAD
"###);
insta::assert_snapshot!(stdout, @r###"
branchless: running command: <git-executable> fetch origin refs/heads/feature
Skipped 1 commit (already up-to-date): feature
"###);
}
Ok(())
}