Module: Rails::Hyperdrive::ThreeWayMerge

Defined in:
lib/rails/hyperdrive/three_way_merge.rb

Overview

Tempfiles live in the system tmpdir, never in the app, so a dry run stays write-free. Fail-open: a missing or erroring git reads as :unavailable, never an exception.

Defined Under Namespace

Classes: Outcome

Class Method Summary collapse

Class Method Details

.binary?(content) ⇒ Boolean

Returns:

  • (Boolean)


40
41
42
43
44
# File 'lib/rails/hyperdrive/three_way_merge.rb', line 40

def binary?(content)
  s = content.to_s
  return true if s.include?("\0".b)
  !s.dup.force_encoding(Encoding::UTF_8).valid_encoding?
end

.merge(ours:, base:, theirs:) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/rails/hyperdrive/three_way_merge.rb', line 18

def merge(ours:, base:, theirs:)
  return Outcome.new(status: :unavailable) if [ours, base, theirs].any? { |b| binary?(b) }

  Dir.mktmpdir("hyperdrive-merge") do |dir|
    paths = { "ours" => ours, "base" => base, "theirs" => theirs }.map do |name, content|
      File.join(dir, name).tap { |p| File.binwrite(p, content) }
    end

    out, _err, status = Open3.capture3("git", "merge-file", "-p", *paths)
    if status.success?
      Outcome.new(status: :clean, body: out)
    elsif status.exited? && status.exitstatus.between?(1, 127)
      # git merge-file exits with the conflict count.
      Outcome.new(status: :conflicted)
    else
      Outcome.new(status: :unavailable)
    end
  end
rescue SystemCallError, IOError
  Outcome.new(status: :unavailable)
end