1package cmd
2
3import (
4	"errors"
5	"fmt"
6
7	"github.com/git-town/git-town/src/drivers"
8	"github.com/git-town/git-town/src/git"
9	"github.com/git-town/git-town/src/prompt"
10	"github.com/git-town/git-town/src/steps"
11)
12
13// These variables represent command-line flags.
14var (
15	allFlag,
16	debugFlag,
17	dryRunFlag,
18	globalFlag bool
19	prodRepo = git.NewProdRepo()
20)
21
22// These variables are set at build time.
23var (
24	version   string
25	buildDate string
26)
27
28const dryRunFlagDescription = "Print the commands but don't run them"
29
30func validateIsConfigured(repo *git.ProdRepo) error {
31	err := prompt.EnsureIsConfigured(repo)
32	if err != nil {
33		return err
34	}
35	return repo.RemoveOutdatedConfiguration()
36}
37
38// ValidateIsRepository asserts that the current directory is in a Git repository.
39// If so, it also navigates to the root directory.
40func ValidateIsRepository(repo *git.ProdRepo) error {
41	if repo.Silent.IsRepository() {
42		return repo.NavigateToRootIfNecessary()
43	}
44	return errors.New("this is not a Git repository")
45}
46
47func getAppendStepList(config appendConfig, repo *git.ProdRepo) (result steps.StepList, err error) {
48	for _, branchName := range append(config.ancestorBranches, config.parentBranch) {
49		steps, err := steps.GetSyncBranchSteps(branchName, true, repo)
50		if err != nil {
51			return result, err
52		}
53		result.AppendList(steps)
54	}
55	result.Append(&steps.CreateBranchStep{BranchName: config.targetBranch, StartingPoint: config.parentBranch})
56	result.Append(&steps.SetParentBranchStep{BranchName: config.targetBranch, ParentBranchName: config.parentBranch})
57	result.Append(&steps.CheckoutBranchStep{BranchName: config.targetBranch})
58	if config.hasOrigin && config.shouldNewBranchPush && !config.isOffline {
59		result.Append(&steps.CreateTrackingBranchStep{BranchName: config.targetBranch})
60	}
61	err = result.Wrap(steps.WrapOptions{RunInGitRoot: true, StashOpenChanges: true}, repo)
62	return result, err
63}
64
65// handleUnfinishedState checks for unfinished state on disk, handles it, and signals whether to continue execution of the originally intended steps.
66func handleUnfinishedState(repo *git.ProdRepo, driver drivers.CodeHostingDriver) (quit bool, err error) {
67	runState, err := steps.LoadPreviousRunState(repo)
68	if err != nil {
69		return false, fmt.Errorf("cannot load previous run state: %w", err)
70	}
71	if runState == nil || !runState.IsUnfinished() {
72		return false, nil
73	}
74	response, err := prompt.AskHowToHandleUnfinishedRunState(
75		runState.Command,
76		runState.UnfinishedDetails.EndBranch,
77		runState.UnfinishedDetails.EndTime,
78		runState.UnfinishedDetails.CanSkip,
79	)
80	if err != nil {
81		return quit, err
82	}
83	switch response {
84	case prompt.ResponseTypeDiscard:
85		err = steps.DeletePreviousRunState(repo)
86		return false, err
87	case prompt.ResponseTypeContinue:
88		hasConflicts, err := repo.Silent.HasConflicts()
89		if err != nil {
90			return false, err
91		}
92		if hasConflicts {
93			return false, fmt.Errorf("you must resolve the conflicts before continuing")
94		}
95		return true, steps.Run(runState, repo, driver)
96	case prompt.ResponseTypeAbort:
97		abortRunState := runState.CreateAbortRunState()
98		return true, steps.Run(&abortRunState, repo, driver)
99	case prompt.ResponseTypeSkip:
100		skipRunState := runState.CreateSkipRunState()
101		return true, steps.Run(&skipRunState, repo, driver)
102	default:
103		return false, fmt.Errorf("unknown response: %s", response)
104	}
105}
106