git-flow-next Documentation

repository·main·Indexed 18 days ago

https://github.com/gittower/git-flow-next

A modern, Go-based implementation of the Git-flow branching model. It supports Classic GitFlow, GitHub Flow, GitLab Flow, and custom workflows using a branch dependency model. Features include shorthand commands for topic branches, a system for custom hooks and filters, and support for various branch types including feature, release, hotfix, and support.

Tokens
76K
Snippets
239
Records
341
Agent score
58%

What's inside git-flow-next

  1. Overview of git-flow-next commands

    main

    The git-flow CLI provides a suite of commands for managing repository workflows, including initialization, configuration, and topic branch management.

    Core Commands:

    • git-flow: Main command entry point.
    • git-flow init: Initializes the git-flow workflow in a repository.
    • git-flow config: Manages the tool's configuration.
    • git-flow overview: Displays the current status of the repository.
    • git-flow integrate: Integrates a base branch into its parent.
    • git-flow completion: Generates shell completion scripts for your terminal.
  2. Use `git.Open` for in-process repository operations

    main

    For tests that exercise internal packages (like internal/git, internal/config, or internal/hooks) without running external binaries, do not rely on the process working directory. Instead, open an explicit *git.Repo handle for the test repository.

    All repository-bound operations should be methods on this handle. This allows tests to run safely in parallel using t.Parallel() because they do not depend on a shared global working directory.

    func TestExample(t *testing.T) {
        t.Parallel() 
        dir := testutil.SetupTestRepo(t)
        defer testutil.CleanupTestRepo(t, dir)
    
        repo, err := git.Open(dir)
        if err != nil {
            t.Fatalf("git.Open failed: %v", err)
        }
    
        // All operations are bound to the specific 'dir'
        if err := repo.Checkout("develop"); err != nil {
            t.Fatal(err)
        }
    
        cfg, err := config.Load(repo) // Reads config from 'dir'
        // ...
    }
  3. Use partial name matching for branch checkout

    main

    The git flow checkout command attempts to resolve branch names using the following priority:

    1. Exact Match: If a branch name matches your input exactly, it is selected.
    2. Prefix Match: If no exact match exists, it looks for branches starting with the provided string.
    3. Ambiguous Match: If multiple branches match the prefix, the command fails and lists the possible matches.

    Example of Ambiguity Error: If you have feature/user-authentication and feature/user-profile, running git flow feature checkout user will result in: Error: Ambiguous branch name 'user'. Matches: feature/user-authentication, feature/user-profile. Please specify a more complete name.

    # If branches exist: feature/user-authentication, feature/user-profile, feature/api-endpoints
    
    git flow feature checkout user    # Fails (Ambiguous)
    git flow feature checkout user-a  # Matches feature/user-authentication
    git flow feature checkout api     # Matches feature/api-endpoints
  4. Understand GitFlow configuration precedence

    main

    When configuring gitflow, settings are applied in the following order of priority (highest to lowest):

    1. Command-line flags (Layer 3): One-off overrides provided during execution.
    2. gitflow.*type*.finish.* (Layer 2): Operational command settings defined in config.
    3. gitflow.branch.*type*.upstreamstrategy (Layer 1): Base branch type process characteristics.
  5. Standard operations for git-flow-avh branch types

    main

    Most branch types (such as bugfix, feature, hotfix, release, and support) support a standard set of operations to manage their lifecycle:

    Operation | Description
    ----------|------------
    list      | List existing branches of this type
    start     | Start a new branch
    finish    | Finish/merge a branch
    publish   | Push branch to remote origin
    track     | Start tracking a remote branch
    diff      | Show changes compared to parent
    rebase    | Rebase branch on parent
    checkout  | Switch to branch
    pull      | Pull branch from remote
    delete    | Delete branch
    rename    | Rename branch
  6. Implement the Configuration Precedence Hierarchy

    main

    Commands must resolve configuration using a three-layer precedence hierarchy. Command-line arguments (Layer 3) always have the highest priority and must win.

    1. Layer 1: Branch Configuration Defaults: Values from gitconfig under gitflow.branch.* (e.g., gitflow.branch.release.tag). These define the identity/process of a branch type.
    2. Layer 2: Command-Specific Git Config Overrides: Values from gitconfig under gitflow.<branchtype>.<command>.* (e.g., gitflow.release.finish.notag). These are persistent operational settings.
    3. Layer 3: Command-Line Arguments: Explicit flags passed during execution (e.g., --tag).

    Implementation Requirement: Use pointer types (e.g., *bool, *string) for command-line options in your options structs. This allows you to distinguish between a flag that was not provided (nil) and a flag that was explicitly set to a zero value (e.g., false).

    // 1. Start with branch configuration default (Layer 1)
    shouldTag := branchConfig.Tag
    
    // 2. Check for branch-specific config override (Layer 2)
    branchSpecificConfig, err := git.GetConfig(fmt.Sprintf("gitflow.%s.finish.notag", branchType))
    if err == nil && branchSpecificConfig == "true" {
        shouldTag = false
    }
    
    // 3. Command-line flags override everything (Layer 3 - WINS)
    if tagOptions != nil && tagOptions.ShouldTag != nil {
        shouldTag = *tagOptions.ShouldTag
    }
  7. Configure merge strategy hierarchy

    main

    Merge strategy behavior is determined by a three-layer configuration hierarchy. Settings in higher layers override those in lower layers.

    Layer 1: Branch Defaults (Lowest Priority)

    Set the base strategy for a specific branch type using: gitflow.branch.<topic>.upstreamstrategy = merge|rebase|squash

    Layer 2: Command-Specific Overrides

    Set persistent overrides for specific commands (like finish) for a branch type:

    • gitflow.<topic>.finish.rebase = true|false
    • gitflow.<topic>.finish.preserve-merges = true|false
    • gitflow.<topic>.finish.ff = true|false
    • gitflow.<topic>.finish.squash = true|false

    Layer 3: Command-Line Flags (Highest Priority)

    Explicitly passing flags during command execution overrides all configuration settings.

  8. Create a Spec Issue for implementation

    main

    A Spec Issue is the implementation-ready version of a feature or bug fix. It serves as the single source of truth for what will be built. Implementation begins from a spec issue, and the resulting Pull Request is verified against it.

    Key Characteristics:

    • Describes changes at a concept level (what to achieve) rather than an implementation level (how to code it).
    • Test scenarios are the centerpiece: Every spec must define concrete scenarios (setup $\rightarrow$ action $\rightarrow$ expected outcome) that cover the happy path, error conditions, and edge cases.
    • Relation to User Reports: When a bug or feature report is accepted, a Spec Issue is created as a child issue (using GitHub's native sub-issue feature) of the original report. The report is the parent; the spec is the child.

    Required Structure:

    1. Summary: Brief overview and a link to the originating report (e.g., Refs #N).
    2. Goal: A short paragraph on what is to be achieved.
    3. Expected Behavior: Concrete descriptions (commands, output, comparisons).
    4. Test Scenarios: A list of concrete scenarios that can be turned into tests.

    Optional Sections:

    • Out of Scope: What the spec deliberately does not cover.
    • Technical Notes: Only details needed to remove ambiguity (affected components, config keys, etc.).
    ### Test Scenarios
    
    1. <Setup / precondition> — <action> — <expected outcome>
    2. ...
  9. Configure Topic Branch Behavior

    main

    Behavior for topic branches is controlled through a three-layered configuration system. You can define how branches are structured and how they behave during lifecycle events.

    Layer 1: Branch Type Configuration (Structural & Process)

    Defines the identity and characteristics of a branch type:

    • Parent branch: The base branch to branch from (structural).
    • Start point: The specific location where the branch is created (structural).
    • Merge strategies: How changes flow upstream and downstream (process).
    • Tag creation: Whether the branch type produces tags upon finish (process).
    • Child branch updates: Whether child base branches are automatically updated after a branch is finished (process).

    Layer 2: Command-specific Configuration

    Controls operational details for specific commands, such as:

    • Fetch behavior.
    • Tag signing.
    • Branch retention policies.

    Layer 3: CLI Flags

    Provides one-off overrides for any configuration setting during a specific command execution.

  10. How the finish command works (State Machine)

    main

    The finish command uses a step-based state machine to manage complex, multi-step operations that might be interrupted by merge conflicts. This ensures consistency and allows for resuming work.

    Execution Steps:

    1. merge
    2. create_tag
    3. update_children
    4. delete_branch

    Key Features:

    • Resumability: State is persisted to disk (mergestate.MergeState). If a conflict occurs, you can resolve it and then use --continue to resume, or use --abort to cancel.
    • Progress Feedback: Provides clear feedback during each step of the lifecycle.
  11. Understand the git-flow configuration hierarchy

    main

    git-flow-next uses a three-layer hierarchy to resolve configuration settings. Understanding this helps you know where to define branch identities versus operational tweaks.

    1. Layer 1: Branch Type Definition (gitflow.branch.*name*.*property*) Defines the identity and process characteristics of a branch type (e.g., what it is, its parent, its prefix, and whether it produces tags). This is for essential structural properties.

    2. Layer 2: Command-Specific Configuration (gitflow.*branchtype*.*command*.*option*) Controls how commands execute for a specific branch type. These are operational settings (e.g., fetch, sign, keep, push-options) that adjust behavior without changing the branch's identity. Layer 2 can override Layer 1 (e.g., notag can override a branch's tag=true setting).

    3. Layer 3: Command-Line Flags Always take the highest precedence and override both configuration layers. Use these for one-off overrides during a single command execution.

  12. Implement the Universal Command Architecture

    main

    All commands in git-flow-next must follow a strict three-layer pattern to separate CLI handling, error/exit code management, and business logic. This ensures consistent error reporting and exit codes across the tool.

    1. Layer 1: Cobra Command Handler: Responsible for parsing flags and arguments from the CLI, then calling the Command Wrapper.
    2. Layer 2: Command Wrapper: Handles error conversion (mapping errors to specific errors.ExitCode values) and manages process exit codes via os.Exit.
    3. Layer 3: Execute Function: Contains the actual business logic. It must return structured errors only and should not handle process exits or direct CLI parsing.
    // Layer 1: Cobra Command Handler
    RunE: func(cmd *cobra.Command, args []string) error {
        param1, _ := cmd.Flags().GetString("flag1")
        param2, _ := cmd.Flags().GetBool("flag2")
        CommandName(param1, param2)
        return nil
    }
    
    // Layer 2: Command Wrapper
    func CommandName(param1 string, param2 bool) {
        if err := executeCommand(param1, param2); err != nil {
            var exitCode errors.ExitCode
            if flowErr, ok := err.(errors.Error); ok {
                exitCode = flowErr.ExitCode()
            } else {
                exitCode = errors.ExitCodeGitError
            }
            fmt.Fprintf(os.Stderr, "Error: %v\n", err)
            os.Exit(int(exitCode))
        }
    }
    
    // Layer 3: Execute Function
    func executeCommand(param1 string, param2 bool) error {
        // Business logic here
        return nil
    }