git-branchless-hook 0.10.0

Supporting library for git-branchless
Documentation
use lib::testing::{make_git, make_git_worktree, GitRunOptions, GitWorktreeWrapper};

#[test]
fn test_is_rebase_underway() -> eyre::Result<()> {
    let git = make_git()?;

    git.init_repo()?;
    let repo = git.get_repo()?;
    assert!(!repo.is_rebase_underway()?);

    let oid1 = git.commit_file_with_contents("test", 1, "foo")?;
    git.run(&["checkout", "HEAD^"])?;
    git.commit_file_with_contents("test", 1, "bar")?;
    git.run_with_options(
        &["rebase", &oid1.to_string()],
        &GitRunOptions {
            expected_exit_code: 1,
            ..Default::default()
        },
    )?;
    assert!(repo.is_rebase_underway()?);

    Ok(())
}

#[test]
fn test_rebase_no_process_new_commits_until_conclusion() -> eyre::Result<()> {
    let git = make_git()?;

    if !git.supports_reference_transactions()? {
        return Ok(());
    }
    git.init_repo()?;

    git.detach_head()?;
    git.commit_file("test1", 1)?;
    let test2_oid = git.commit_file("test2", 2)?;

    // Ensure commits aren't preserved if the rebase is aborted.
    {
        git.run_with_options(
            &["rebase", "master", "--force", "--exec", "exit 1"],
            &GitRunOptions {
                expected_exit_code: 1,
                ..Default::default()
            },
        )?;
        git.run(&[
            "commit",
            "--amend",
            "--message",
            "this commit shouldn't show up in the smartlog",
        ])?;
        git.commit_file("test3", 3)?;
        git.run(&["rebase", "--abort"])?;

        {
            let stdout = git.smartlog()?;
            insta::assert_snapshot!(stdout, @r###"
        O f777ecc (master) create initial.txt
        |
        o 62fc20d create test1.txt
        |
        @ 96d1c37 create test2.txt
        "###);
        }
    }

    // Ensure commits are preserved if the rebase succeeds.
    {
        git.run(&["checkout", "HEAD^"])?;

        {
            let (stdout, stderr) = git.run_with_options(
                &["rebase", "master", "--force", "--exec", "exit 1"],
                &GitRunOptions {
                    expected_exit_code: 1,
                    ..Default::default()
                },
            )?;

            // As of `38c541ce94048cf72aa4f465be9314423a57f445` (Git >=v2.36.0),
            // `git checkout` is called in fewer cases, which affects the stderr
            // output for the test.
            let stderr: String = stderr
                .lines()
                .filter_map(|line| {
                    if line.starts_with("branchless:") {
                        None
                    } else {
                        Some(format!("{line}\n"))
                    }
                })
                .collect();

            insta::assert_snapshot!(stderr, @r###"
            Executing: exit 1
            warning: execution failed: exit 1
            You can fix the problem, and then run

              git rebase --continue


            "###);
            insta::assert_snapshot!(stdout, @"");
        }

        git.commit_file("test4", 4)?;
        {
            let (stdout, stderr) = git.run(&["rebase", "--continue"])?;
            insta::assert_snapshot!(stderr, @r###"
            branchless: processing 1 rewritten commit
            branchless: This operation abandoned 1 commit!
            branchless: Consider running one of the following:
            branchless:   - git restack: re-apply the abandoned commits/branches
            branchless:     (this is most likely what you want to do)
            branchless:   - git smartlog: assess the situation
            branchless:   - git hide [<commit>...]: hide the commits from the smartlog
            branchless:   - git undo: undo the operation
            hint: disable this hint by running: git config --global branchless.hint.restackWarnAbandoned false
            Successfully rebased and updated detached HEAD.
            "###);
            insta::assert_snapshot!(stdout, @"");
        }

        // Switch away to make sure that the new commit isn't visible just
        // because it's reachable from `HEAD`.
        git.run(&["checkout", &test2_oid.to_string()])?;

        {
            let stdout = git.smartlog()?;
            insta::assert_snapshot!(stdout, @r###"
            O f777ecc (master) create initial.txt
            |\
            | o 047b7ad create test1.txt
            | |
            | o ecab41f create test4.txt
            |
            x 62fc20d (rewritten as 047b7ad7) create test1.txt
            |
            @ 96d1c37 create test2.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_hooks_in_worktree() -> eyre::Result<()> {
    let git = make_git()?;

    if !git.supports_reference_transactions()? {
        return Ok(());
    }
    git.init_repo()?;

    git.commit_file("test1", 1)?;
    git.detach_head()?;

    let GitWorktreeWrapper {
        temp_dir: _temp_dir,
        worktree,
    } = make_git_worktree(&git, "new-worktree")?;

    {
        let (stdout, stderr) =
            worktree.run(&["commit", "--allow-empty", "-m", "new empty commit"])?;
        insta::assert_snapshot!(stderr, @r###"
        branchless: processing 1 update: ref HEAD
        branchless: processed commit: 1bed0d8 new empty commit
        "###);
        insta::assert_snapshot!(stdout, @r###"
        [detached HEAD 1bed0d8] new empty commit
        "###);
    }

    {
        let stdout = git.smartlog()?;
        insta::assert_snapshot!(stdout, @r###"
        :
        @ 62fc20d (master) create test1.txt
        |
        o 1bed0d8 new empty commit
        "###);
    }
    {
        let stdout = worktree.smartlog()?;
        insta::assert_snapshot!(stdout, @r###"
        :
        O 62fc20d (master) create test1.txt
        |
        @ 1bed0d8 new empty commit
        "###);
    }

    {
        let (stdout, stderr) =
            worktree.run(&["commit", "--amend", "--allow-empty", "--message", "amended"])?;
        insta::assert_snapshot!(stderr, @r###"
        branchless: processing 1 update: ref HEAD
        branchless: processed commit: cc4313e amended
        branchless: processing 1 rewritten commit
        "###);
        insta::assert_snapshot!(stdout, @r###"
        [detached HEAD cc4313e] amended
         Date: Thu Oct 29 12:34:56 2020 +0000
        "###);
    }
    {
        let stdout = git.smartlog()?;
        insta::assert_snapshot!(stdout, @r###"
        :
        @ 62fc20d (master) create test1.txt
        |
        o cc4313e amended
        "###);
    }
    {
        let stdout = worktree.smartlog()?;
        insta::assert_snapshot!(stdout, @r###"
        :
        O 62fc20d (master) create test1.txt
        |
        @ cc4313e amended
        "###);
    }

    Ok(())
}