Class: Ace::GitCommit::Molecules::SplitCommitExecutor

Inherits:
Object
  • Object
show all
Defined in:
lib/ace/git_commit/molecules/split_commit_executor.rb

Overview

SplitCommitExecutor performs sequential commits with rollback support

Constant Summary collapse

DEFAULT_SCOPE_NAME =

Reference the default scope name constant

Ace::Support::Config::Models::ConfigGroup::DEFAULT_SCOPE_NAME

Instance Method Summary collapse

Constructor Details

#initialize(git_executor:, diff_analyzer:, file_stager:, message_generator:) ⇒ SplitCommitExecutor

Returns a new instance of SplitCommitExecutor.



10
11
12
13
14
15
# File 'lib/ace/git_commit/molecules/split_commit_executor.rb', line 10

def initialize(git_executor:, diff_analyzer:, file_stager:, message_generator:)
  @git = git_executor
  @diff_analyzer = diff_analyzer
  @file_stager = file_stager
  @message_generator = message_generator
end

Instance Method Details

#execute(groups, options) ⇒ Models::SplitCommitResult

Execute split commits

Parameters:

Returns:



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/ace/git_commit/molecules/split_commit_executor.rb', line 21

def execute(groups, options)
  original_head = current_head
  result = Models::SplitCommitResult.new(original_head: original_head)

  # Pre-generate all messages in batch if using LLM (includes ordering)
  if options.use_llm?
    batch_result = generate_batch_messages(groups, options)
    ordered_groups, messages = reorder_groups_by_llm(groups, batch_result)
  else
    ordered_groups = groups
    messages = Array.new(groups.length) { options.message }
  end

  ordered_groups.each_with_index do |group, index|
    label = group.scope_name.to_s.empty? ? DEFAULT_SCOPE_NAME : group.scope_name
    puts "[#{index + 1}/#{ordered_groups.length}] Committing #{label} changes..." unless options.quiet

    unless @file_stager.stage_paths(group.files, quiet: options.quiet)
      error_msg = @file_stager.last_error || "Failed to stage files"
      result.add_failure(group, error_msg)
      unless options.quiet
        warn "✗ Failed to stage files for scope '#{label}':"
        warn "  #{error_msg}"
      end
      rollback_to(original_head, result, options)
      return result
    end

    # Check if all files were gitignored - skip commit for this group
    if @file_stager.all_files_skipped?
      unless options.quiet
        puts "✓ No files to commit for scope '#{label}' (all gitignored)"
      end
      result.add_skipped(group, "All files gitignored")
      next
    end

    message = messages[index]

    if options.dry_run
      show_group_dry_run(message, group)
      result.add_dry_run(group)
      next
    end

    commit_sha = perform_commit(message, options)
    result.add_success(group, commit_sha)
  rescue GitError => e
    scope_label = group.scope_name.to_s.empty? ? DEFAULT_SCOPE_NAME : group.scope_name
    error_msg = "Failed to commit scope '#{scope_label}': #{e.message}"
    result.add_failure(group, error_msg)
    unless options.quiet
      warn "✗ #{error_msg}"
    end
    rollback_to(original_head, result, options)
    return result
  end

  result
end