diff --git a/README.md b/README.md index fc51052f..b5ffc111 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Once you've done some work on the first branch within the stack, at some point y - Give the branch a name. - Select the branch to create the new branch from. -The new branch will be created from the branch at the bottom of the stack and the current branch will be changed so you can make more changes. +The new branch will be created from the selected parent branch and the current branch will be changed so you can make more changes. If a new branch was not able to be pushed to the remote, you can use the `stack push` command to push the branch to the remote later. diff --git a/src/Stack.Tests/Commands/Branch/AddBranchCommandHandlerTests.cs b/src/Stack.Tests/Commands/Branch/AddBranchCommandHandlerTests.cs index 4c745f9d..dd7f036c 100644 --- a/src/Stack.Tests/Commands/Branch/AddBranchCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Branch/AddBranchCommandHandlerTests.cs @@ -13,41 +13,46 @@ namespace Stack.Tests.Commands.Branch; public class AddBranchCommandHandlerTests(ITestOutputHelper testOutputHelper) { [Fact] - public async Task WhenNoInputsProvided_AsksForStackAndBranchAndConfirms_AddsBranchToStack() + public async Task WhenNoInputsProvided_AsksForStackAndBranchAndParentBranchAndConfirms_AddsBranchToStack() { // Arrange var sourceBranch = Some.BranchName(); - var anotherBranch = Some.BranchName(); + var firstBranch = Some.BranchName(); + var childBranch = Some.BranchName(); var branchToAdd = Some.BranchName(); var remoteUri = Some.HttpsUri().ToString(); - var stackConfig = new TestStackConfigBuilder() - .WithStack(stack => stack - .WithName("Stack1") - .WithRemoteUri(remoteUri) - .WithSourceBranch(sourceBranch) - .WithBranch(branch => branch.WithName(anotherBranch))) - .WithStack(stack => stack.WithName("Stack2").WithRemoteUri(remoteUri).WithSourceBranch(sourceBranch)) - .Build(); var inputProvider = Substitute.For(); var logger = new TestLogger(testOutputHelper); var gitClient = Substitute.For(); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(sourceBranch); - gitClient.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(new[] { sourceBranch, anotherBranch, branchToAdd }); + gitClient.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(new[] { sourceBranch, firstBranch, childBranch, branchToAdd }); gitClient.DoesLocalBranchExist(branchToAdd).Returns(true); + var stackConfig = new TestStackConfigBuilder() + .WithStack(stack => stack + .WithName("Stack1") + .WithRemoteUri(remoteUri) + .WithSourceBranch(sourceBranch) + .WithBranch(branch => branch.WithName(firstBranch).WithChildBranch(child => child.WithName(childBranch)))) + .WithStack(stack => stack + .WithName("Stack2") + .WithRemoteUri(remoteUri) + .WithSourceBranch(sourceBranch)) + .Build(); var handler = new AddBranchCommandHandler(inputProvider, logger, gitClient, stackConfig); inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns(branchToAdd); + inputProvider.Select(Questions.SelectParentBranch, Arg.Any()).Returns(firstBranch); // Act - await handler.Handle(AddBranchCommandInputs.Empty); + await handler.Handle(new AddBranchCommandInputs(null, null, null)); // Assert stackConfig.Stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, sourceBranch, [new Config.Branch(anotherBranch, [new Config.Branch(branchToAdd, [])])]), + new("Stack1", remoteUri, sourceBranch, [new Config.Branch(firstBranch, [new Config.Branch(childBranch, []), new Config.Branch(branchToAdd, [])])]), new("Stack2", remoteUri, sourceBranch, []) }); } @@ -79,6 +84,7 @@ public async Task WhenStackNameProvided_DoesNotAskForStackName_AddsBranchFromSta var handler = new AddBranchCommandHandler(inputProvider, logger, gitClient, stackConfig); inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns(branchToAdd); + inputProvider.Select(Questions.SelectParentBranch, Arg.Any()).Returns(anotherBranch); // Act await handler.Handle(new AddBranchCommandInputs("Stack1", null, null)); @@ -118,6 +124,7 @@ public async Task WhenOnlyOneStackExists_DoesNotAskForStackName_AddsBranchFromSt var handler = new AddBranchCommandHandler(inputProvider, logger, gitClient, stackConfig); inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns(branchToAdd); + inputProvider.Select(Questions.SelectParentBranch, Arg.Any()).Returns(anotherBranch); // Act await handler.Handle(new AddBranchCommandInputs(null, null, null)); @@ -191,6 +198,7 @@ public async Task WhenBranchNameProvided_DoesNotAskForBranchName_AddsBranchFromS var handler = new AddBranchCommandHandler(inputProvider, logger, gitClient, stackConfig); inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); + inputProvider.Select(Questions.SelectParentBranch, Arg.Any()).Returns(anotherBranch); // Act await handler.Handle(new AddBranchCommandInputs(null, branchToAdd, null)); @@ -305,7 +313,7 @@ public async Task WhenAllInputsProvided_DoesNotAskForAnything_AddsBranchFromStac var handler = new AddBranchCommandHandler(inputProvider, logger, gitClient, stackConfig); // Act - await handler.Handle(new AddBranchCommandInputs("Stack1", branchToAdd, null)); + await handler.Handle(new AddBranchCommandInputs("Stack1", branchToAdd, anotherBranch)); // Assert stackConfig.Stacks.Should().BeEquivalentTo(new List @@ -317,7 +325,7 @@ public async Task WhenAllInputsProvided_DoesNotAskForAnything_AddsBranchFromStac } [Fact] - public async Task WhenV2Schema_AndParentBranchNotProvided_AsksForParentBranch_CreatesNewBranchUnderneathParent() + public async Task WhenParentBranchProvided_DoesNotAskForParentBranch_CreatesNewBranchUnderneathParent() { // Arrange var sourceBranch = Some.BranchName(); @@ -334,53 +342,6 @@ public async Task WhenV2Schema_AndParentBranchNotProvided_AsksForParentBranch_Cr gitClient.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(new[] { sourceBranch, firstBranch, childBranch, branchToAdd }); gitClient.DoesLocalBranchExist(branchToAdd).Returns(true); var stackConfig = new TestStackConfigBuilder() - .WithSchemaVersion(SchemaVersion.V2) - .WithStack(stack => stack - .WithName("Stack1") - .WithRemoteUri(remoteUri) - .WithSourceBranch(sourceBranch) - .WithBranch(branch => branch.WithName(firstBranch).WithChildBranch(child => child.WithName(childBranch)))) - .WithStack(stack => stack - .WithName("Stack2") - .WithRemoteUri(remoteUri) - .WithSourceBranch(sourceBranch)) - .Build(); - var handler = new AddBranchCommandHandler(inputProvider, logger, gitClient, stackConfig); - - inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); - inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns(branchToAdd); - inputProvider.Select(Questions.SelectParentBranch, Arg.Any()).Returns(firstBranch); - - // Act - await handler.Handle(new AddBranchCommandInputs(null, null, null)); - - // Assert - stackConfig.Stacks.Should().BeEquivalentTo(new List - { - new("Stack1", remoteUri, sourceBranch, [new Config.Branch(firstBranch, [new Config.Branch(childBranch, []), new Config.Branch(branchToAdd, [])])]), - new("Stack2", remoteUri, sourceBranch, []) - }); - } - - [Fact] - public async Task WhenV2Schema_AndParentBranchProvided_DoesNotAskForParentBranch_CreatesNewBranchUnderneathParent() - { - // Arrange - var sourceBranch = Some.BranchName(); - var firstBranch = Some.BranchName(); - var childBranch = Some.BranchName(); - var branchToAdd = Some.BranchName(); - var remoteUri = Some.HttpsUri().ToString(); - - var inputProvider = Substitute.For(); - var logger = new TestLogger(testOutputHelper); - var gitClient = Substitute.For(); - gitClient.GetRemoteUri().Returns(remoteUri); - gitClient.GetCurrentBranch().Returns(sourceBranch); - gitClient.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(new[] { sourceBranch, firstBranch, childBranch, branchToAdd }); - gitClient.DoesLocalBranchExist(branchToAdd).Returns(true); - var stackConfig = new TestStackConfigBuilder() - .WithSchemaVersion(SchemaVersion.V2) .WithStack(stack => stack .WithName("Stack1") .WithRemoteUri(remoteUri) @@ -408,44 +369,4 @@ public async Task WhenV2Schema_AndParentBranchProvided_DoesNotAskForParentBranch inputProvider.DidNotReceive().Select(Questions.SelectParentBranch, Arg.Any()); } - - [Fact] - public async Task WhenV1Schema_AndParentBranchProvided_ThrowsException() - { - // Arrange - var sourceBranch = Some.BranchName(); - var firstBranch = Some.BranchName(); - var childBranch = Some.BranchName(); - var branchToAdd = Some.BranchName(); - var remoteUri = Some.HttpsUri().ToString(); - - var inputProvider = Substitute.For(); - var logger = new TestLogger(testOutputHelper); - var gitClient = Substitute.For(); - gitClient.GetRemoteUri().Returns(remoteUri); - gitClient.GetCurrentBranch().Returns(sourceBranch); - gitClient.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(new[] { sourceBranch, firstBranch, childBranch, branchToAdd }); - gitClient.DoesLocalBranchExist(branchToAdd).Returns(true); - var stackConfig = new TestStackConfigBuilder() - .WithSchemaVersion(SchemaVersion.V1) - .WithStack(stack => stack - .WithName("Stack1") - .WithRemoteUri(remoteUri) - .WithSourceBranch(sourceBranch) - .WithBranch(branch => branch.WithName(firstBranch).WithChildBranch(child => child.WithName(childBranch)))) - .WithStack(stack => stack - .WithName("Stack2") - .WithRemoteUri(remoteUri) - .WithSourceBranch(sourceBranch)) - .Build(); - var handler = new AddBranchCommandHandler(inputProvider, logger, gitClient, stackConfig); - - inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); - inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns(branchToAdd); - - // Act and assert - await handler.Invoking(h => h.Handle(new AddBranchCommandInputs(null, null, firstBranch))) - .Should().ThrowAsync() - .WithMessage("Parent branches are not supported in stacks with schema version v1. Please migrate the stack to v2 format."); - } } diff --git a/src/Stack.Tests/Commands/Branch/NewBranchCommandHandlerTests.cs b/src/Stack.Tests/Commands/Branch/NewBranchCommandHandlerTests.cs index b18b0f83..32f04d37 100644 --- a/src/Stack.Tests/Commands/Branch/NewBranchCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Branch/NewBranchCommandHandlerTests.cs @@ -13,11 +13,12 @@ namespace Stack.Tests.Commands.Branch; public class NewBranchCommandHandlerTests(ITestOutputHelper testOutputHelper) { [Fact] - public async Task WhenNoInputsProvided_AsksForStackAndBranch_CreatesAndAddsBranchToStack_PushesToRemote_AndSwitchesToBranch() + public async Task WhenNoInputsProvided_AsksForStackAndBranchAndParentBranch_CreatesAndAddsBranchToStack_PushesToRemote_AndSwitchesToBranch() { // Arrange var sourceBranch = Some.BranchName(); - var anotherBranch = Some.BranchName(); + var firstBranch = Some.BranchName(); + var childBranch = Some.BranchName(); var newBranch = Some.BranchName(); var remoteUri = Some.HttpsUri().ToString(); @@ -29,7 +30,7 @@ public async Task WhenNoInputsProvided_AsksForStackAndBranch_CreatesAndAddsBranc .WithName("Stack1") .WithRemoteUri(remoteUri) .WithSourceBranch(sourceBranch) - .WithBranch(branch => branch.WithName(anotherBranch))) + .WithBranch(branch => branch.WithName(firstBranch).WithChildBranch(child => child.WithName(childBranch)))) .WithStack(stack => stack .WithName("Stack2") .WithRemoteUri(remoteUri) @@ -39,10 +40,10 @@ public async Task WhenNoInputsProvided_AsksForStackAndBranch_CreatesAndAddsBranc inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); inputProvider.Text(Questions.BranchName, Arg.Any()).Returns(newBranch); - + inputProvider.Select(Questions.SelectParentBranch, Arg.Any()).Returns(firstBranch); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(sourceBranch); - gitClient.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(new[] { sourceBranch, anotherBranch }); + gitClient.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(new[] { sourceBranch, firstBranch, childBranch }); gitClient.DoesLocalBranchExist(newBranch).Returns(false); // Act @@ -51,11 +52,10 @@ public async Task WhenNoInputsProvided_AsksForStackAndBranch_CreatesAndAddsBranc // Assert stackConfig.Stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, sourceBranch, [new Config.Branch(anotherBranch, [new Config.Branch(newBranch, [])])]), + new("Stack1", remoteUri, sourceBranch, [new Config.Branch(firstBranch, [new Config.Branch(childBranch, []), new Config.Branch(newBranch, [])])]), new("Stack2", remoteUri, sourceBranch, []) }); - gitClient.Received().CreateNewBranch(newBranch, anotherBranch); - gitClient.Received().PushNewBranch(newBranch); + gitClient.Received().CreateNewBranch(newBranch, firstBranch); gitClient.Received().ChangeBranch(newBranch); } @@ -85,6 +85,8 @@ public async Task WhenStackNameProvided_DoesNotAskForStackName_CreatesAndAddsBra var handler = new NewBranchCommandHandler(inputProvider, logger, gitClient, stackConfig); inputProvider.Text(Questions.BranchName, Arg.Any()).Returns(newBranch); + inputProvider.Select(Questions.SelectParentBranch, Arg.Any()).Returns(anotherBranch); + gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(sourceBranch); gitClient.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(new[] { sourceBranch, anotherBranch }); @@ -124,6 +126,8 @@ public async Task WhenOnlyOneStackExists_DoesNotAskForStackName_CreatesAndAddsBr var handler = new NewBranchCommandHandler(inputProvider, logger, gitClient, stackConfig); inputProvider.Text(Questions.BranchName, Arg.Any()).Returns(newBranch); + inputProvider.Select(Questions.SelectParentBranch, Arg.Any()).Returns(anotherBranch); + gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(sourceBranch); gitClient.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(new[] { sourceBranch, anotherBranch }); @@ -203,6 +207,8 @@ public async Task WhenBranchNameProvided_DoesNotAskForBranchName_CreatesAndAddsB var handler = new NewBranchCommandHandler(inputProvider, logger, gitClient, stackConfig); inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); + inputProvider.Select(Questions.SelectParentBranch, Arg.Any()).Returns(anotherBranch); + gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(sourceBranch); gitClient.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(new[] { sourceBranch, anotherBranch }); @@ -325,6 +331,8 @@ public async Task WhenPushToTheRemoteFails_StillCreatesTheBranchLocallyAndAddsIt inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); inputProvider.Text(Questions.BranchName, Arg.Any()).Returns(newBranch); + inputProvider.Select(Questions.SelectParentBranch, Arg.Any()).Returns(anotherBranch); + gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(sourceBranch); gitClient.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(new[] { sourceBranch, anotherBranch }); @@ -344,7 +352,7 @@ public async Task WhenPushToTheRemoteFails_StillCreatesTheBranchLocallyAndAddsIt } [Fact] - public async Task WhenV2Schema_AndParentBranchNotProvided_AsksForParentBranch_CreatesNewBranchUnderneathParent() + public async Task WhenParentBranchNotProvided_AsksForParentBranch_CreatesNewBranchUnderneathParent() { // Arrange var sourceBranch = Some.BranchName(); @@ -357,7 +365,6 @@ public async Task WhenV2Schema_AndParentBranchNotProvided_AsksForParentBranch_Cr var logger = new TestLogger(testOutputHelper); var gitClient = Substitute.For(); var stackConfig = new TestStackConfigBuilder() - .WithSchemaVersion(SchemaVersion.V2) .WithStack(stack => stack .WithName("Stack1") .WithRemoteUri(remoteUri) @@ -392,7 +399,7 @@ public async Task WhenV2Schema_AndParentBranchNotProvided_AsksForParentBranch_Cr } [Fact] - public async Task WhenV2Schema_AndParentBranchProvided_DoesNotAskForParentBranch_CreatesNewBranchUnderneathParent() + public async Task WhenParentBranchProvided_DoesNotAskForParentBranch_CreatesNewBranchUnderneathParent() { // Arrange var sourceBranch = Some.BranchName(); @@ -405,7 +412,6 @@ public async Task WhenV2Schema_AndParentBranchProvided_DoesNotAskForParentBranch var logger = new TestLogger(testOutputHelper); var gitClient = Substitute.For(); var stackConfig = new TestStackConfigBuilder() - .WithSchemaVersion(SchemaVersion.V2) .WithStack(stack => stack .WithName("Stack1") .WithRemoteUri(remoteUri) @@ -439,43 +445,4 @@ public async Task WhenV2Schema_AndParentBranchProvided_DoesNotAskForParentBranch inputProvider.DidNotReceive().Select(Questions.SelectParentBranch, Arg.Any()); } - - [Fact] - public async Task WhenV1Schema_AndParentBranchProvided_ThrowsException() - { - // Arrange - var sourceBranch = Some.BranchName(); - var firstBranch = Some.BranchName(); - var childBranch = Some.BranchName(); - var newBranch = Some.BranchName(); - var remoteUri = Some.HttpsUri().ToString(); - - var inputProvider = Substitute.For(); - var logger = new TestLogger(testOutputHelper); - var gitClient = Substitute.For(); - var stackConfig = new TestStackConfigBuilder() - .WithSchemaVersion(SchemaVersion.V1) - .WithStack(stack => stack - .WithName("Stack1") - .WithRemoteUri(remoteUri) - .WithSourceBranch(sourceBranch) - .WithBranch(branch => branch.WithName(firstBranch).WithChildBranch(child => child.WithName(childBranch)))) - .WithStack(stack => stack - .WithName("Stack2") - .WithRemoteUri(remoteUri) - .WithSourceBranch(sourceBranch)) - .Build(); - var handler = new NewBranchCommandHandler(inputProvider, logger, gitClient, stackConfig); - - inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); - inputProvider.Text(Questions.BranchName, Arg.Any()).Returns(newBranch); - gitClient.GetRemoteUri().Returns(remoteUri); - gitClient.GetCurrentBranch().Returns(sourceBranch); - gitClient.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(new[] { sourceBranch, firstBranch, childBranch }); - - // Act and assert - await handler.Invoking(h => h.Handle(new NewBranchCommandInputs(null, null, firstBranch))) - .Should().ThrowAsync() - .WithMessage("Parent branches are not supported in stacks with schema version v1. Please migrate the stack to v2 format."); - } } diff --git a/src/Stack.Tests/Commands/Branch/RemoveBranchCommandHandlerTests.cs b/src/Stack.Tests/Commands/Branch/RemoveBranchCommandHandlerTests.cs index fbf031c0..433a5f11 100644 --- a/src/Stack.Tests/Commands/Branch/RemoveBranchCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Branch/RemoveBranchCommandHandlerTests.cs @@ -13,7 +13,7 @@ namespace Stack.Tests.Commands.Branch; public class RemoveBranchCommandHandlerTests(ITestOutputHelper testOutputHelper) { [Fact] - public async Task WhenNoInputsProvided_AsksForStackAndBranchAndConfirms_RemovesBranchFromStack() + public async Task WhenNoInputsProvided_AsksForAllInputsAndConfirms_RemovesBranchFromStack() { // Arrange var sourceBranch = Some.BranchName(); @@ -41,6 +41,8 @@ public async Task WhenNoInputsProvided_AsksForStackAndBranchAndConfirms_RemovesB inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns(branchToRemove); + inputProvider.Select(Questions.RemoveBranchChildAction, Arg.Any(), Arg.Any>()) + .Returns(RemoveBranchChildAction.RemoveChildren); inputProvider.Confirm(Questions.ConfirmRemoveBranch).Returns(true); // Act @@ -291,7 +293,7 @@ public async Task WhenConfirmProvided_DoesNotAskForConfirmation_RemovesBranchFro } [Fact] - public async Task WhenSchemaIsV2_AndChildActionIsMoveChildrenToParent_RemovesBranchAndMovesChildrenToParent() + public async Task WhenChildActionIsMoveChildrenToParent_RemovesBranchAndMovesChildrenToParent() { // Arrange var sourceBranch = Some.BranchName(); @@ -300,7 +302,6 @@ public async Task WhenSchemaIsV2_AndChildActionIsMoveChildrenToParent_RemovesBra var remoteUri = Some.HttpsUri().ToString(); var stackConfig = new TestStackConfigBuilder() - .WithSchemaVersion(SchemaVersion.V2) .WithStack(stack => stack .WithName("Stack1") .WithRemoteUri(remoteUri) @@ -332,7 +333,7 @@ public async Task WhenSchemaIsV2_AndChildActionIsMoveChildrenToParent_RemovesBra } [Fact] - public async Task WhenSchemaIsV2_AndChildActionIsRemoveChildren_RemovesBranchAndDeletesChildren() + public async Task WhenChildActionIsRemoveChildren_RemovesBranchAndDeletesChildren() { // Arrange var sourceBranch = Some.BranchName(); @@ -341,7 +342,6 @@ public async Task WhenSchemaIsV2_AndChildActionIsRemoveChildren_RemovesBranchAnd var remoteUri = Some.HttpsUri().ToString(); var stackConfig = new TestStackConfigBuilder() - .WithSchemaVersion(SchemaVersion.V2) .WithStack(stack => stack .WithName("Stack1") .WithRemoteUri(remoteUri) @@ -373,46 +373,7 @@ public async Task WhenSchemaIsV2_AndChildActionIsRemoveChildren_RemovesBranchAnd } [Fact] - public async Task WhenSchemaIsV1_RemovesBranchAndMovesChildrenToParent() - { - // Arrange - var sourceBranch = Some.BranchName(); - var branchToRemove = Some.BranchName(); - var childBranch = Some.BranchName(); - var remoteUri = Some.HttpsUri().ToString(); - - var stackConfig = new TestStackConfigBuilder() - .WithSchemaVersion(SchemaVersion.V1) - .WithStack(stack => stack - .WithName("Stack1") - .WithRemoteUri(remoteUri) - .WithSourceBranch(sourceBranch) - .WithBranch(b => b.WithName(branchToRemove).WithChildBranch(b => b.WithName(childBranch)))) - .Build(); - var inputProvider = Substitute.For(); - var logger = new TestLogger(testOutputHelper); - var gitClient = Substitute.For(); - var handler = new RemoveBranchCommandHandler(inputProvider, logger, gitClient, stackConfig); - - gitClient.GetRemoteUri().Returns(remoteUri); - gitClient.GetCurrentBranch().Returns(sourceBranch); - - inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); - inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns(branchToRemove); - inputProvider.Confirm(Questions.ConfirmRemoveBranch).Returns(true); - - // Act - await handler.Handle(RemoveBranchCommandInputs.Empty); - - // Assert - stackConfig.Stacks.Should().BeEquivalentTo(new List - { - new("Stack1", remoteUri, sourceBranch, [new Config.Branch(childBranch, [])]) - }); - } - - [Fact] - public async Task WhenSchemaIsV2_AndRemoveChildrenIsProvided_RemovesBranchAndDeletesChildren() + public async Task WhenRemoveChildrenIsProvided_RemovesBranchAndDeletesChildren() { // Arrange var sourceBranch = Some.BranchName(); @@ -421,7 +382,6 @@ public async Task WhenSchemaIsV2_AndRemoveChildrenIsProvided_RemovesBranchAndDel var remoteUri = Some.HttpsUri().ToString(); var stackConfig = new TestStackConfigBuilder() - .WithSchemaVersion(SchemaVersion.V2) .WithStack(stack => stack .WithName("Stack1") .WithRemoteUri(remoteUri) @@ -455,7 +415,7 @@ public async Task WhenSchemaIsV2_AndRemoveChildrenIsProvided_RemovesBranchAndDel } [Fact] - public async Task WhenSchemaIsV2_AndMoveChildrenToParentIsProvided_RemovesBranchAndMovesChildrenToParent() + public async Task WhenMoveChildrenToParentIsProvided_RemovesBranchAndMovesChildrenToParent() { // Arrange var sourceBranch = Some.BranchName(); @@ -464,7 +424,6 @@ public async Task WhenSchemaIsV2_AndMoveChildrenToParentIsProvided_RemovesBranch var remoteUri = Some.HttpsUri().ToString(); var stackConfig = new TestStackConfigBuilder() - .WithSchemaVersion(SchemaVersion.V2) .WithStack(stack => stack .WithName("Stack1") .WithRemoteUri(remoteUri) diff --git a/src/Stack.Tests/Config/FileStackConfigTests.cs b/src/Stack.Tests/Config/FileStackConfigTests.cs index c24c7ec3..9ba05b34 100644 --- a/src/Stack.Tests/Config/FileStackConfigTests.cs +++ b/src/Stack.Tests/Config/FileStackConfigTests.cs @@ -23,7 +23,7 @@ public void Load_WhenConfigFileDoesNotExist_ReturnsEmptyList() var stackData = fileStackConfig.Load(); // Assert - stackData.Should().BeEquivalentTo(new StackData(SchemaVersion.V2, [])); + stackData.Should().BeEquivalentTo(new StackData([])); } [Fact] @@ -67,7 +67,7 @@ public void Load_WhenConfigFileIsInV1Format_LoadsCorrectly_MigratesAndSavesFileI var stackData = fileStackConfig.Load(); // Assert - stackData.Should().BeEquivalentTo(new StackData(SchemaVersion.V2, [expectedStack])); + stackData.Should().BeEquivalentTo(new StackData([expectedStack])); var savedJson = File.ReadAllText(configPath); var expectedJson = $@"{{ ""SchemaVersion"": 2, @@ -162,62 +162,7 @@ public void Load_WhenConfigFileIsInV2Format_LoadsCorrectly() var stackData = fileStackConfig.Load(); // Assert - stackData.Should().BeEquivalentTo(new StackData(SchemaVersion.V2, [expectedStack])); - } - - [Fact] - public void Save_WhenConfigFileIsInV1Format_AndAllStacksHaveASingleTree_SavesCorrectlyInV1Format() - { - // Arrange - using var tempDirectory = TemporaryDirectory.Create(); - var configPath = Path.Combine(tempDirectory.DirectoryPath, "stack", "config.json"); - var stackName = Some.Name(); - var remoteUri = Some.HttpsUri().ToString(); - var sourceBranch = Some.BranchName(); - var branch1 = Some.BranchName(); - var branch2 = Some.BranchName(); - - // Create an empty config file - Directory.CreateDirectory(Path.GetDirectoryName(configPath)!); - File.WriteAllText(configPath, "[]"); - - // Create a Stack object with a simple tree structure that should be saved in V1 format - var stack = new Config.Stack( - stackName, - remoteUri, - sourceBranch, - [ - new(branch1, - [ - new(branch2, []) - ]) - ]); - - // Act - var fileStackConfig = new FileStackConfig(tempDirectory.DirectoryPath); - fileStackConfig.Save(new StackData(SchemaVersion.V1, [stack])); - - // Assert - var savedJson = File.ReadAllText(configPath); - - // Format the expected JSON string with proper indentation - var expectedJson = $@"[ - {{ - ""Name"": ""{stackName}"", - ""RemoteUri"": ""{remoteUri}"", - ""SourceBranch"": ""{sourceBranch}"", - ""Branches"": [ - ""{branch1}"", - ""{branch2}"" - ] - }} -]"; - - // Normalize both JSON strings to remove whitespace differences - var normalizedSavedJson = NormalizeJsonString(savedJson); - var normalizedExpectedJson = NormalizeJsonString(expectedJson); - - normalizedSavedJson.Should().Be(normalizedExpectedJson); + stackData.Should().BeEquivalentTo(new StackData([expectedStack])); } [Fact] @@ -258,7 +203,7 @@ public void Save_WhenConfigFileIsInV1Format_AndStackIsChangedToHaveTreeStructure var fileStackConfig = new FileStackConfig(tempDirectory.DirectoryPath); // Act - fileStackConfig.Save(new StackData(SchemaVersion.V2, [stack])); + fileStackConfig.Save(new StackData([stack])); // Assert var savedJson = File.ReadAllText(configPath); diff --git a/src/Stack.Tests/Helpers/TestStackConfigBuilder.cs b/src/Stack.Tests/Helpers/TestStackConfigBuilder.cs index 29c46f63..dc0d282c 100644 --- a/src/Stack.Tests/Helpers/TestStackConfigBuilder.cs +++ b/src/Stack.Tests/Helpers/TestStackConfigBuilder.cs @@ -7,7 +7,6 @@ namespace Stack.Tests.Helpers; public class TestStackConfigBuilder { readonly List> stackBuilders = []; - SchemaVersion schemaVersion = SchemaVersion.V1; public TestStackConfigBuilder WithStack(Action stackBuilder) { @@ -15,16 +14,10 @@ public TestStackConfigBuilder WithStack(Action stackBuilder) return this; } - public TestStackConfigBuilder WithSchemaVersion(SchemaVersion version) - { - schemaVersion = version; - return this; - } - public TestStackConfig Build() { return new TestStackConfig( - new StackData(schemaVersion, [.. stackBuilders.Select(builder => + new StackData([.. stackBuilders.Select(builder => { var stackBuilder = new TestStackBuilder(); builder(stackBuilder); diff --git a/src/Stack/Commands/Branch/AddBranchCommand.cs b/src/Stack/Commands/Branch/AddBranchCommand.cs index 1011f437..b0b4803b 100644 --- a/src/Stack/Commands/Branch/AddBranchCommand.cs +++ b/src/Stack/Commands/Branch/AddBranchCommand.cs @@ -60,11 +60,6 @@ public override async Task Handle(AddBranchCommandInputs inputs) return; } - if (stackData.SchemaVersion == SchemaVersion.V1 && inputs.ParentBranchName is not null) - { - throw new InvalidOperationException("Parent branches are not supported in stacks with schema version v1. Please migrate the stack to v2 format."); - } - var stack = inputProvider.SelectStack(logger, inputs.StackName, stacksForRemote, currentBranch); if (stack is null) @@ -86,22 +81,14 @@ public override async Task Handle(AddBranchCommandInputs inputs) Branch? sourceBranch = null; - if (stackData.SchemaVersion == SchemaVersion.V1) - { - // In V1 schema there is only a single set of branches, we always add to the end. - sourceBranch = stack.GetAllBranches().LastOrDefault(); - } - if (stackData.SchemaVersion == SchemaVersion.V2) - { - var parentBranchName = inputProvider.SelectParentBranch(logger, inputs.ParentBranchName, stack); + var parentBranchName = inputProvider.SelectParentBranch(logger, inputs.ParentBranchName, stack); - if (parentBranchName != stack.SourceBranch) + if (parentBranchName != stack.SourceBranch) + { + sourceBranch = stack.GetAllBranches().FirstOrDefault(b => b.Name.Equals(parentBranchName, StringComparison.OrdinalIgnoreCase)); + if (sourceBranch is null) { - sourceBranch = stack.GetAllBranches().FirstOrDefault(b => b.Name.Equals(parentBranchName, StringComparison.OrdinalIgnoreCase)); - if (sourceBranch is null) - { - throw new InvalidOperationException($"Branch '{parentBranchName}' not found in stack '{stack.Name}'."); - } + throw new InvalidOperationException($"Branch '{parentBranchName}' not found in stack '{stack.Name}'."); } } diff --git a/src/Stack/Commands/Branch/NewBranchCommand.cs b/src/Stack/Commands/Branch/NewBranchCommand.cs index 4be3957d..505bf0c1 100644 --- a/src/Stack/Commands/Branch/NewBranchCommand.cs +++ b/src/Stack/Commands/Branch/NewBranchCommand.cs @@ -62,11 +62,6 @@ public override async Task Handle(NewBranchCommandInputs inputs) return; } - if (stackData.SchemaVersion == SchemaVersion.V1 && inputs.ParentBranchName is not null) - { - throw new InvalidOperationException("Parent branches are not supported in stacks with schema version v1. Please migrate the stack to v2 format."); - } - var stack = inputProvider.SelectStack(logger, inputs.StackName, stacksForRemote, currentBranch); if (stack is null) @@ -88,22 +83,14 @@ public override async Task Handle(NewBranchCommandInputs inputs) Branch? sourceBranch = null; - if (stackData.SchemaVersion == SchemaVersion.V1) - { - // In V1 schema there is only a single set of branches, we always add to the end. - sourceBranch = stack.GetAllBranches().LastOrDefault(); - } - if (stackData.SchemaVersion == SchemaVersion.V2) - { - var parentBranchName = inputProvider.SelectParentBranch(logger, inputs.ParentBranchName, stack); + var parentBranchName = inputProvider.SelectParentBranch(logger, inputs.ParentBranchName, stack); - if (parentBranchName != stack.SourceBranch) + if (parentBranchName != stack.SourceBranch) + { + sourceBranch = stack.GetAllBranches().FirstOrDefault(b => b.Name.Equals(parentBranchName, StringComparison.OrdinalIgnoreCase)); + if (sourceBranch is null) { - sourceBranch = stack.GetAllBranches().FirstOrDefault(b => b.Name.Equals(parentBranchName, StringComparison.OrdinalIgnoreCase)); - if (sourceBranch is null) - { - throw new InvalidOperationException($"Branch '{parentBranchName}' not found in stack '{stack.Name}'."); - } + throw new InvalidOperationException($"Branch '{parentBranchName}' not found in stack '{stack.Name}'."); } } diff --git a/src/Stack/Commands/Branch/RemoveBranchCommand.cs b/src/Stack/Commands/Branch/RemoveBranchCommand.cs index 3430e24d..1ccbdaa7 100644 --- a/src/Stack/Commands/Branch/RemoveBranchCommand.cs +++ b/src/Stack/Commands/Branch/RemoveBranchCommand.cs @@ -87,17 +87,12 @@ public override async Task Handle(RemoveBranchCommandInputs inputs) throw new InvalidOperationException($"Branch '{branchName}' not found in stack '{stack.Name}'."); } - var action = RemoveBranchChildAction.MoveChildrenToParent; - - if (stackData.SchemaVersion == SchemaVersion.V2) - { - action = - inputs.RemoveChildrenAction ?? - inputProvider.Select( - Questions.RemoveBranchChildAction, - [RemoveBranchChildAction.MoveChildrenToParent, RemoveBranchChildAction.RemoveChildren], - (action) => action.Humanize()); - } + var action = + inputs.RemoveChildrenAction ?? + inputProvider.Select( + Questions.RemoveBranchChildAction, + [RemoveBranchChildAction.MoveChildrenToParent, RemoveBranchChildAction.RemoveChildren], + (action) => action.Humanize()); if (inputs.Confirm || inputProvider.Confirm(Questions.ConfirmRemoveBranch)) { diff --git a/src/Stack/Commands/Helpers/StackHelpers.cs b/src/Stack/Commands/Helpers/StackHelpers.cs index d7fcb1f8..40ddae85 100644 --- a/src/Stack/Commands/Helpers/StackHelpers.cs +++ b/src/Stack/Commands/Helpers/StackHelpers.cs @@ -239,19 +239,17 @@ public static StackStatus GetStackStatus( } public static void OutputStackStatus( - SchemaVersion schemaVersion, List statuses, ILogger logger) { foreach (var status in statuses) { - OutputStackStatus(schemaVersion, status, logger); + OutputStackStatus(status, logger); logger.NewLine(); } } public static void OutputStackStatus( - SchemaVersion schemaVersion, StackStatus status, ILogger logger, Func? getBranchPullRequestDisplay = null) @@ -264,11 +262,6 @@ public static void OutputStackStatus( items.Add(GetBranchAndPullRequestStatusOutput(branch, getBranchPullRequestDisplay)); } - if (schemaVersion == SchemaVersion.V1 && items.Count > 0) - { - items = [.. MoreEnumerable.TraverseDepthFirst(items.First(), i => i.Children).Select(i => new TreeItem(i.Value, []))]; - } - logger.Information(status.Name.Stack()); logger.Tree(new Tree(header, [.. items])); } diff --git a/src/Stack/Commands/PullRequests/CreatePullRequestsCommand.cs b/src/Stack/Commands/PullRequests/CreatePullRequestsCommand.cs index 545e229d..ff76b9e7 100644 --- a/src/Stack/Commands/PullRequests/CreatePullRequestsCommand.cs +++ b/src/Stack/Commands/PullRequests/CreatePullRequestsCommand.cs @@ -97,7 +97,7 @@ public override async Task Handle(CreatePullRequestsCommandInputs inputs) } } - StackHelpers.OutputStackStatus(stackData.SchemaVersion, status, logger); + StackHelpers.OutputStackStatus(status, logger); logger.NewLine(); @@ -128,7 +128,7 @@ public override async Task Handle(CreatePullRequestsCommandInputs inputs) logger.NewLine(); - OutputUpdatedStackStatus(logger, stackData.SchemaVersion, stack, status, pullRequestInformation); + OutputUpdatedStackStatus(logger, stack, status, pullRequestInformation); logger.NewLine(); @@ -191,13 +191,11 @@ private static List CreatePullRequests( private static void OutputUpdatedStackStatus( ILogger logger, - SchemaVersion schemaVersion, Config.Stack stack, StackStatus status, List pullRequestInformation) { StackHelpers.OutputStackStatus( - schemaVersion, status, logger, (branch) => diff --git a/src/Stack/Commands/Remote/SyncStackCommand.cs b/src/Stack/Commands/Remote/SyncStackCommand.cs index 451acc83..cf4fc4df 100644 --- a/src/Stack/Commands/Remote/SyncStackCommand.cs +++ b/src/Stack/Commands/Remote/SyncStackCommand.cs @@ -101,7 +101,7 @@ public override async Task Handle(SyncStackCommandInputs inputs) gitHubClient, true); - StackHelpers.OutputStackStatus(stackData.SchemaVersion, status, logger); + StackHelpers.OutputStackStatus(status, logger); logger.NewLine(); diff --git a/src/Stack/Commands/Stack/StackStatusCommand.cs b/src/Stack/Commands/Stack/StackStatusCommand.cs index d62ed471..5aa449d6 100644 --- a/src/Stack/Commands/Stack/StackStatusCommand.cs +++ b/src/Stack/Commands/Stack/StackStatusCommand.cs @@ -115,7 +115,7 @@ protected override async Task ExecuteAndReturnRespon protected override void WriteDefaultOutput(StackStatusCommandResponse response) { - StackHelpers.OutputStackStatus(response.SchemaVersion, response.Stacks, StdOutLogger); + StackHelpers.OutputStackStatus(response.Stacks, StdOutLogger); if (response.Stacks.Count == 1) { @@ -188,7 +188,7 @@ private static StackStatusCommandJsonOutputBranchDetail MapBranchDetail(BranchDe } public record StackStatusCommandInputs(string? Stack, bool All, bool Full); -public record StackStatusCommandResponse(SchemaVersion SchemaVersion, List Stacks); +public record StackStatusCommandResponse(List Stacks); public class StackStatusCommandHandler( IInputProvider inputProvider, @@ -233,6 +233,6 @@ public override async Task Handle(StackStatusCommand gitHubClient, inputs.Full); - return new StackStatusCommandResponse(stackData.SchemaVersion, stackStatusResults); + return new StackStatusCommandResponse(stackStatusResults); } } \ No newline at end of file diff --git a/src/Stack/Config/StackConfig.cs b/src/Stack/Config/StackConfig.cs index cd1801dc..3795479f 100644 --- a/src/Stack/Config/StackConfig.cs +++ b/src/Stack/Config/StackConfig.cs @@ -3,7 +3,7 @@ namespace Stack.Config; -public record StackData(SchemaVersion SchemaVersion, List Stacks); +public record StackData(List Stacks); public interface IStackConfig { @@ -35,18 +35,18 @@ public StackData Load() var stacksFile = GetConfigPath(); if (!File.Exists(stacksFile)) { - return new StackData(SchemaVersion.V2, []); + return new StackData([]); } var jsonString = File.ReadAllText(stacksFile); if (IsStackConfigInV2Format(jsonString)) { - return new StackData(SchemaVersion.V2, LoadStacksFromV2Format(jsonString)); + return new StackData(LoadStacksFromV2Format(jsonString)); } // If no schema version, this means v1 format - migrate to v2 format and re-save before returning var stacksV1 = LoadStacksFromV1Format(jsonString); - var stacks = new StackData(SchemaVersion.V2, stacksV1); + var stacks = new StackData(stacksV1); Save(stacks); return stacks; } @@ -59,19 +59,7 @@ public void Save(StackData stackData) Directory.CreateDirectory(Path.GetDirectoryName(stacksFile)!); } - if (stackData.SchemaVersion == SchemaVersion.V1) - { - if (stackData.Stacks.Any(s => !s.HasSingleTree)) - { - throw new InvalidOperationException("Cannot save in v1 format if any stack has multiple trees."); - } - - // If all stacks have a single tree and we are still in v1 format continue to preference this. - File.WriteAllText(stacksFile, JsonSerializer.Serialize(stackData.Stacks.Select(MapToV1Format).ToList(), StackConfigJsonSerializerContext.Default.ListStackV1)); - return; - } - - // Else we are writing in v2 format + // We only write in v2 format var existingConfigFileIsInV1Format = false; if (File.Exists(stacksFile))