Class: Onair::Git

Inherits:
Object
  • Object
show all
Defined in:
lib/onair/git.rb

Overview

The only place that shells out to git. Everything else takes an instance as a dependency — this is the seam unit tests fake. All methods degrade to nil/false/[] on failure; they never raise.

Defined Under Namespace

Classes: Identity

Instance Method Summary collapse

Constructor Details

#initialize(fetch_allowed: true, dir: nil) ⇒ Git

Returns a new instance of Git.



12
13
14
15
16
# File 'lib/onair/git.rb', line 12

def initialize(fetch_allowed: true, dir: nil)
  @fetch_allowed = fetch_allowed
  @dir = dir
  @fetched = false
end

Instance Method Details

#ancestor?(ancestor_sha, descendant_sha) ⇒ Boolean

Returns:

  • (Boolean)


50
51
52
# File 'lib/onair/git.rb', line 50

def ancestor?(ancestor_sha, descendant_sha)
  success?("merge-base", "--is-ancestor", ancestor_sha, descendant_sha)
end

#commit_info(sha) ⇒ Object



42
43
44
45
46
47
48
# File 'lib/onair/git.rb', line 42

def commit_info(sha)
  out = capture("log", "-1", "--format=%s%x09%an%x09%ae%x09%ct", sha)
  return nil if out.nil?

  subject, name, email, epoch = out.chomp.split("\t", 4)
  CommitInfo.new(subject:, author_name: name, author_email: email, committed_at: Time.at(epoch.to_i))
end

#count_between(from_sha, to_sha) ⇒ Object



54
55
56
# File 'lib/onair/git.rb', line 54

def count_between(from_sha, to_sha)
  capture("rev-list", "--count", "#{from_sha}..#{to_sha}")&.strip&.to_i
end

#fetch_once!Object

Best-effort, at most once per process, skipped under --no-fetch. The commit may still be absent afterward (force-pushed away); callers degrade via the "(commit not found)" fallback.



35
36
37
38
39
40
# File 'lib/onair/git.rb', line 35

def fetch_once!
  return if @fetched || !@fetch_allowed

  @fetched = true
  success?("fetch", "--quiet", "origin")
end

#first_parent_below(sha, count) ⇒ Object

The first-parent commits just below the given head (head itself excluded), as [sha, author_name, author_email] tuples.



60
61
62
63
64
65
# File 'lib/onair/git.rb', line 60

def first_parent_below(sha, count)
  out = capture("log", "--first-parent", "--skip=1", "-#{count}", "--format=%H%x09%an%x09%ae", sha)
  return [] if out.nil?

  out.lines.map { |line| line.chomp.split("\t", 3) }
end

#has_commit?(sha) ⇒ Boolean

Returns:

  • (Boolean)


28
29
30
# File 'lib/onair/git.rb', line 28

def has_commit?(sha)
  success?("cat-file", "-e", "#{sha}^{commit}")
end

#identityObject



67
68
69
# File 'lib/onair/git.rb', line 67

def identity
  Identity.new(name: capture("config", "user.name")&.strip, email: capture("config", "user.email")&.strip)
end

#origin_repoObject

"owner/name" parsed from the origin remote, or nil.



72
73
74
75
# File 'lib/onair/git.rb', line 72

def origin_repo
  url = capture("remote", "get-url", "origin")&.strip
  url&.match(%r{github\.com[:/](?<repo>[^/]+/[^/\s]+?)(?:\.git)?/?\z})&.[](:repo)
end

#remote_head(branch) ⇒ Object

Tip of origin/ straight from the remote — accurate without a pull. Falls back to the local origin/ ref when offline.



20
21
22
23
24
25
26
# File 'lib/onair/git.rb', line 20

def remote_head(branch)
  out = capture("ls-remote", "origin", "refs/heads/#{branch}")
  sha = out.to_s[/\A\h+/]
  return sha unless sha.nil? || sha.empty?

  capture("rev-parse", "origin/#{branch}")&.strip
end