Skip to content

Commit

Permalink
Integration tests for commit squashing
Browse files Browse the repository at this point in the history
  • Loading branch information
krlvi committed Jan 8, 2025
1 parent 044f764 commit bc07919
Show file tree
Hide file tree
Showing 3 changed files with 265 additions and 0 deletions.
2 changes: 2 additions & 0 deletions crates/gitbutler-branch-actions/tests/branch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ mod extra;
mod reorder;

mod dependencies;

mod squash;
54 changes: 54 additions & 0 deletions crates/gitbutler-branch-actions/tests/fixtures/squash.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env bash
set -eu -o pipefail
CLI=${1:?The first argument is the GitButler CLI}


git init remote
(cd remote
echo a > file
git add . && git commit -m "init"
)

export GITBUTLER_CLI_DATA_DIR=../user/gitbutler/app-data

# Scenario:
# - commit 5 (a-branch-3)
# - commit 4 (a-branch-2)
# - commit 3
# - commit 2
# - commit 1 (a-branch-1)
git clone remote multiple-commits
(cd multiple-commits
git config user.name "Author"
git config user.email "[email protected]"

git branch existing-branch
$CLI project add --switch-to-workspace "$(git rev-parse --symbolic-full-name @{u})"

# Create the stack
$CLI branch create --set-default my_stack

# Change 1 is in the default stacked branch
echo change1 >> file1
$CLI branch commit my_stack -m "commit 1"

# Create a new stacked branch
$CLI branch series my_stack -s "a-branch-2"

# Changes 2 and 3 are in the same file, with 2 overwriting 3
echo change2 > file2_3
$CLI branch commit my_stack -m "commit 2"
echo change3 > file2_3
$CLI branch commit my_stack -m "commit 3"

# Commit 4 is in a different file
echo change4 > file4
$CLI branch commit my_stack -m "commit 4"

# Create a new stacked branch
$CLI branch series my_stack -s "a-branch-3"

# Commit 5 is in a different file
echo change5 >> file5
$CLI branch commit my_stack -m "commit 5"
)
209 changes: 209 additions & 0 deletions crates/gitbutler-branch-actions/tests/squash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
use anyhow::Result;
use gitbutler_branch_actions::{internal::PatchSeries, list_virtual_branches, squash_commits};
use gitbutler_command_context::CommandContext;
use gitbutler_project::Project;
use gitbutler_stack::{
stack_context::{CommandContextExt, StackContext},
StackBranch, VirtualBranchesHandle,
};
use itertools::Itertools;
use tempfile::TempDir;

// Squash commit into another commit below it that not an immediate parent
// Also asserts: Squash commit that is a stacked branch head makes the parent the new head
//
// - commit 5 (a-branch-3)
// - commit 4 (a-branch-2) ──┐
// - commit 3 │
// - commit 2 ◄──┘
// - commit 1 (a-branch-1)
//
// Result:
// - commit 5 (a-branch-3)
// - commit 3 (a-branch-2)
// - commit 2+4
// - commit 1 (a-branch-1)
#[test]
fn squash_below() -> Result<()> {
let (ctx, _temp_dir) = command_ctx()?;
let stack_ctx = ctx.to_stack_context()?;
let test = test_ctx(&ctx, &stack_ctx)?;
squash_commits(
ctx.project(),
test.stack.id,
vec![test.commit_4.id()],
test.commit_2.id(),
)?;

let branches = list_branches(ctx.project())?;
assert_eq!(branches.branch_1.patches[0].description, "commit 1");
Ok(())
}

// Squash commit into another commit above it that not an immediate parent
// Also asserts: Squash commit that is a stacked branch head can leave an empty branch if it was the only commit
// Also asserts: Squash commit that can result in the bottom stacked branch becoming empty
//
// - commit 5 (a-branch-3)
// - commit 4 (a-branch-2)
// - commit 3 ◄──┐
// - commit 2 │
// - commit 1 (a-branch-1) ──┘
//
// Result:
// - commit 5 (a-branch-3)
// - commit 4 (a-branch-2)
// - commit 3+1
// - commit 2
// - base (a-branch-1)
#[test]
fn squash_above() -> Result<()> {
Ok(())
}

// Squash commit into another with the result being a conflict returns an error
//
// - commit 5 (a-branch-3)
// - commit 4 (a-branch-2)
// - commit 3 ──┐
// - commit 2 ◄──┘
// - commit 1 (a-branch-1)
//
// Commits 3 and 2 update the same file and line number
//
// NB: We may want to change this behavior in the future
#[test]
fn squash_producting_conflict_errors_out() -> Result<()> {
Ok(())
}

// Squash a commit into a commit that itself is a stacked branch head
//
// - commit 5 (a-branch-3)
// - commit 4 (a-branch-2) ──┐
// - commit 3 │
// - commit 2 │
// - commit 1 (a-branch-1)◄──┘
//
// Result:
// - commit 5 (a-branch-3)
// - commit 3 (a-branch-2)
// - commit 2
// - commit 1+4 (a-branch-1)
#[test]
fn squash_below_into_stack_head() -> Result<()> {
Ok(())
}

// Squash multiple commits into another commit below it that not an immediate parent to either
// Also assert: Squash multiple commits where one is a stacked branch head
//
// - commit 5 (a-branch-3)
// - commit 4 (a-branch-2) ──┐
// - commit 3 │
// - commit 2 ──┤
// - commit 1 (a-branch-1)◄──┘
//
// Result:
// - commit 5 (a-branch-3)
// - commit 3 (a-branch-2)
// - commit 1+4+2 (a-branch-1)
#[test]
fn squash_multiple() -> Result<()> {
Ok(())
}

// Squash multiple commits where both are stacked branch heads
//
// - commit 5 (a-branch-3) ──┐
// - commit 4 (a-branch-2) ──┤
// - commit 3 │
// - commit 2 ◄──┘
// - commit 1 (a-branch-1)
//
// Result:
// (a-branch-3)
// - commit 3 (a-branch-2)
// - commit 2+5+4
// - commit 1 (a-branch-1)
#[test]
fn squash_multiple_from_heads() -> Result<()> {
Ok(())
}

fn command_ctx() -> Result<(CommandContext, TempDir)> {
gitbutler_testsupport::writable::fixture("squash.sh", "multiple-commits")
}

fn test_ctx<'a>(ctx: &'a CommandContext, stack_ctx: &'a StackContext) -> Result<TestContext<'a>> {
let handle = VirtualBranchesHandle::new(ctx.project().gb_dir());
let stacks = handle.list_all_stacks()?;
let stack = stacks.iter().find(|b| b.name == "my_stack").unwrap();
let branches = stack.branches();
let branch_1 = branches.iter().find(|b| b.name == "a-branch-1").unwrap();
let commit_1 = branch_1.commits(stack_ctx, stack)?.local_commits[0].clone();
let branch_2 = branches.iter().find(|b| b.name == "a-branch-2").unwrap();
let commit_2 = branch_2.commits(stack_ctx, stack)?.local_commits[0].clone();
let commit_3 = branch_2.commits(stack_ctx, stack)?.local_commits[1].clone();
let commit_4 = branch_2.commits(stack_ctx, stack)?.local_commits[2].clone();
let branch_3 = branches.iter().find(|b| b.name == "a-branch-3").unwrap();
let commit_5 = branch_3.commits(stack_ctx, stack)?.local_commits[0].clone();
Ok(TestContext {
stack: stack.clone(),
branch_1: branch_1.clone(),
branch_2: branch_2.clone(),
branch_3: branch_3.clone(),
commit_1,
commit_2,
commit_3,
commit_4,
commit_5,
})
}

// The fixture:
// - commit 5 (a-branch-3)
// - commit 4 (a-branch-2)
// - commit 3
// - commit 2
// - commit 1 (a-branch-1)
#[allow(unused)]
struct TestContext<'a> {
stack: gitbutler_stack::Stack,
branch_1: StackBranch,
branch_2: StackBranch,
branch_3: StackBranch,
commit_1: git2::Commit<'a>,
commit_2: git2::Commit<'a>,
commit_3: git2::Commit<'a>,
commit_4: git2::Commit<'a>,
commit_5: git2::Commit<'a>,
}

/// Stack branches, but from the list API
#[derive(Debug, PartialEq, Clone)]
struct TestBranchListing {
branch_1: PatchSeries,
branch_2: PatchSeries,
branch_3: PatchSeries,
}

/// Stack branches from the API
fn list_branches(project: &Project) -> Result<TestBranchListing> {
let branches = list_virtual_branches(project)?
.branches
.first()
.unwrap()
.series
.iter()
.map(|s| s.clone().unwrap())
.collect_vec();
fn find(branches: &[PatchSeries], name: &str) -> PatchSeries {
branches.iter().find(|b| b.name == name).unwrap().clone()
}
Ok(TestBranchListing {
branch_1: find(&branches, "a-branch-1"),
branch_2: find(&branches, "a-branch-2"),
branch_3: find(&branches, "a-branch-3"),
})
}

0 comments on commit bc07919

Please sign in to comment.