-
Notifications
You must be signed in to change notification settings - Fork 552
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Integration tests for commit squashing
- Loading branch information
Showing
3 changed files
with
265 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,3 +5,5 @@ mod extra; | |
mod reorder; | ||
|
||
mod dependencies; | ||
|
||
mod squash; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"), | ||
}) | ||
} |