Class: Rails::Credentials::Conflict::GitConflictHandler

Inherits:
Object
  • Object
show all
Defined in:
lib/rails/credentials/conflict/git_conflict_handler.rb,
sig/rails/credentials/conflict/git_conflict_handler.rbs

Overview

Interacts with git to read conflict stages, detect merge state, and stage resolved files.

Uses git's staging area to access the three versions of a conflicted file (base, ours, theirs) and determines labels for the merge participants.

Constant Summary collapse

STAGE_BASE =

Returns:

  • (Integer)
1
STAGE_OURS =

Returns:

  • (Integer)
2
STAGE_THEIRS =

Returns:

  • (Integer)
3

Instance Method Summary collapse

Constructor Details

#initialize(credentials_path, relative_path) ⇒ GitConflictHandler

Returns a new instance of GitConflictHandler.

Parameters:

  • credentials_path (String, Pathname)
  • relative_path (String, Pathname)


19
20
21
22
# File 'lib/rails/credentials/conflict/git_conflict_handler.rb', line 19

def initialize(credentials_path, relative_path)
  @credentials_path = credentials_path.to_s
  @relative_path = relative_path.to_s
end

Instance Method Details

#build_label(branch_name, commit_sha) ⇒ String

Parameters:

  • branch_name (String)
  • commit_sha (String)

Returns:

  • (String)


92
93
94
# File 'lib/rails/credentials/conflict/git_conflict_handler.rb', line 92

def build_label(branch_name, commit_sha)
  "#{branch_name} (#{commit_sha})"
end

#detect_head_refString

Returns:

  • (String)


83
84
85
86
87
88
89
90
# File 'lib/rails/credentials/conflict/git_conflict_handler.rb', line 83

def detect_head_ref
  %w[MERGE_HEAD CHERRY_PICK_HEAD REBASE_HEAD].each do |ref|
    _, status = Open3.capture2("git", "rev-parse", "--verify", ref)
    return ref if status.success?
  end

  "MERGE_HEAD"
end

#get_current_branchString

Returns:

  • (String)


96
97
98
99
100
# File 'lib/rails/credentials/conflict/git_conflict_handler.rb', line 96

def get_current_branch
  branch, status = Open3.capture2("git", "rev-parse", "--abbrev-ref", "HEAD")
  branch = branch.strip
  status.success? && !branch.empty? ? branch : "HEAD"
end

#get_incoming_branch(head_ref) ⇒ String

Parameters:

  • head_ref (String)

Returns:

  • (String)


102
103
104
105
106
# File 'lib/rails/credentials/conflict/git_conflict_handler.rb', line 102

def get_incoming_branch(head_ref)
  branch, status = Open3.capture2("git", "name-rev", "--name-only", head_ref)
  branch = branch.strip
  status.success? && !branch.empty? ? branch : head_ref
end

#get_merge_base_sha(head_ref) ⇒ String

Parameters:

  • head_ref (String)

Returns:

  • (String)


113
114
115
116
117
118
119
120
121
# File 'lib/rails/credentials/conflict/git_conflict_handler.rb', line 113

def get_merge_base_sha(head_ref)
  ours_sha, s1 = Open3.capture2("git", "rev-parse", "HEAD")
  theirs_sha, s2 = Open3.capture2("git", "rev-parse", head_ref)

  return "unknown" unless s1.success? && s2.success?

  base_sha, s3 = Open3.capture2("git", "merge-base", ours_sha.strip, theirs_sha.strip)
  s3.success? ? base_sha.strip[0..7] : "unknown"
end

#get_merge_labelsmerge_labels

Returns a Hash of labels for the three merge participants (+:ours+, :base, :theirs), each containing a branch name and short SHA.

Returns:

  • (merge_labels)


62
63
64
65
66
67
68
69
70
# File 'lib/rails/credentials/conflict/git_conflict_handler.rb', line 62

def get_merge_labels
  head_ref = detect_head_ref

  {
    ours: build_label(get_current_branch, get_short_sha("HEAD")),
    base: build_label("ancestor", get_merge_base_sha(head_ref)),
    theirs: build_label(get_incoming_branch(head_ref), get_short_sha(head_ref))
  }
end

#get_short_sha(ref) ⇒ String

Parameters:

  • ref (String)

Returns:

  • (String)


108
109
110
111
# File 'lib/rails/credentials/conflict/git_conflict_handler.rb', line 108

def get_short_sha(ref)
  sha, status = Open3.capture2("git", "rev-parse", "--short=8", ref)
  status.success? ? sha.strip : "unknown"
end

#get_version(version) ⇒ String

Retrieves the encrypted content for the given version (:base, :ours, or :theirs) from the git staging area. Returns an empty string on failure.

Parameters:

  • version (version)

Returns:

  • (String)


40
41
42
43
44
45
46
47
# File 'lib/rails/credentials/conflict/git_conflict_handler.rb', line 40

def get_version(version)
  stage_number = stage_for_version(version)
  content, status = Open3.capture2(
    "git", "show", ":#{stage_number}:#{@relative_path}",
    binmode: true
  )
  status.success? ? content.strip : ""
end

#stage_for_version(version) ⇒ Integer

Parameters:

  • version (version)

Returns:

  • (Integer)


74
75
76
77
78
79
80
81
# File 'lib/rails/credentials/conflict/git_conflict_handler.rb', line 74

def stage_for_version(version)
  case version
  when :base then STAGE_BASE
  when :ours then STAGE_OURS
  when :theirs then STAGE_THEIRS
  else raise Error, "Unknown version: #{version}"
  end
end

#stage_resolved_file!void

This method returns an undefined value.

Stages the resolved credentials file with git add. Raises Error if the command fails.

Raises:



51
52
53
54
55
56
57
# File 'lib/rails/credentials/conflict/git_conflict_handler.rb', line 51

def stage_resolved_file!
  _, status = Open3.capture2("git", "add", @credentials_path)

  return if status.success?

  raise Error, "Failed to stage resolved file: #{@credentials_path}"
end

#validate_conflict!void

This method returns an undefined value.

Verifies that the credentials file exists and is in a conflicted state (UU or AA in git status --porcelain). Raises Error otherwise.

Raises:



27
28
29
30
31
32
33
34
35
36
# File 'lib/rails/credentials/conflict/git_conflict_handler.rb', line 27

def validate_conflict!
  raise Error, "Credentials file not found: #{@credentials_path}" unless File.exist?(@credentials_path)

  git_status, status = Open3.capture2("git", "status", "--porcelain", @credentials_path)
  git_status = git_status.strip

  return if status.success? && (git_status.start_with?("UU") || git_status.start_with?("AA"))

  raise Error, "No git conflict detected for #{@credentials_path}"
end