Module: Utils::Git Private

Extended by:
SystemCommand::Mixin
Defined in:
utils/git.rb

Overview

This module is part of a private API. This module may only be used in the Homebrew/brew repository. Third parties should avoid using this module if possible, as it may be removed or changed without warning.

Helper functions for querying Git information.

See Also:

Class Method Summary collapse

Methods included from SystemCommand::Mixin

command_sandbox, system_command, system_command!

Class Method Details

.available? ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:

  • (Boolean)


15
16
17
# File 'utils/git.rb', line 15

def self.available?
  !version.null?
end

.changed_files(repository) ⇒ Array<String>

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

The paths (relative to repository's root) changed in its working tree since it diverged from the upstream default branch. The base is the origin/HEAD merge-base rather than the local default branch ref, which is often stale (e.g. in worktrees and freshly-cloned taps); it falls back to main when origin/HEAD is unavailable.

Parameters:

Returns:



135
136
137
138
139
# File 'utils/git.rb', line 135

def self.changed_files(repository)
  base_ref = Utils.popen_read(git, "-C", repository, "merge-base", "origin/HEAD", "HEAD").chomp.presence
  base_ref ||= "main"
  Utils.popen_read(git, "-C", repository, "diff", "--name-only", "--no-relative", base_ref).split("\n")
end

.cherry_pick!(repo, *args, resolve: false, verbose: false) ⇒ String

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Special case of git cherry-pick that permits non-verbose output and optional resolution on merge conflict.

Parameters:

  • repo (Pathname, String)
  • args (String)
  • resolve (Boolean) (defaults to: false)
  • verbose (Boolean) (defaults to: false)

Returns:



223
224
225
226
227
228
229
230
231
232
233
# File 'utils/git.rb', line 223

def self.cherry_pick!(repo, *args, resolve: false, verbose: false)
  cmd = [git.to_s, "-C", repo, "cherry-pick"] + args
  output = Utils.popen_read(*cmd, err: :out)
  if $CHILD_STATUS.success?
    puts output if verbose
    output
  else
    system git.to_s, "-C", repo.to_s, "cherry-pick", "--abort" unless resolve
    raise ErrorDuringExecution.new(cmd, status: $CHILD_STATUS, output: [[:stdout, output]])
  end
end

.clear_available_cache ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.



64
65
66
67
68
# File 'utils/git.rb', line 64

def self.clear_available_cache
  remove_instance_variable(:@version) if defined?(@version)
  remove_instance_variable(:@path) if defined?(@path)
  remove_instance_variable(:@git) if defined?(@git)
end

.ensure_installed! ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'utils/git.rb', line 168

def self.ensure_installed!
  return if available?

  # Keep Git bootstrapping in sync with cmd/update.sh.
  if CoreTap.instance.installed? || !Homebrew::EnvConfig.no_install_from_api?
    begin
      # Otherwise `git` will be installed from source in tests that need it. This is slow
      # and will also likely fail due to `OS::Linux` and `OS::Mac` being undefined.
      raise "Refusing to install Git on a generic OS." if ENV["HOMEBREW_TEST_GENERIC_OS"]

      require "formula"
      Formula["git"].ensure_installed!
      ENV["HOMEBREW_GIT"] = (HOMEBREW_PREFIX/"opt/git/bin/git").to_s
      clear_available_cache
    rescue
      raise "Git is unavailable"
    end
  end

  raise "Git is unavailable" unless available?
end

.file_at_commit(repo, file, commit) ⇒ String

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:



123
124
125
126
127
# File 'utils/git.rb', line 123

def self.file_at_commit(repo, file, commit)
  relative_file = Pathname(file)
  relative_file = relative_file.relative_path_from(repo) if relative_file.absolute?
  Utils.popen_read(git, "-C", repo, "show", "#{commit}:#{relative_file}")
end

.get_tar_commit_id(file) ⇒ String?

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Extract commit ID using git get-tar-commit-id.

The file can be an uncompressed .tar or gzip-compressed .tar.gz.

Parameters:

Returns:



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'utils/git.rb', line 145

def self.get_tar_commit_id(file)
  file = Pathname(file)
  return unless file.file?
  return unless file.readable?

  # `git get-tar-commit-id` is documented to only read the first 1024 bytes.
  # See https://git-scm.com/docs/git-get-tar-commit-id
  headersize = 1024
  filename = file.basename.to_s
  header = if filename.end_with?(".tar.gz", ".tgz")
    Utils.popen_read("gunzip", "-c", "-q", "--", file) { |io| io.read(headersize) }
  elsif filename.end_with?(".tar")
    file.binread(headersize)
  end
  return if header.nil? || header.size < headersize

  commit = Utils.popen_write(git, "get-tar-commit-id") { |io| io.write(header) }
  return unless $CHILD_STATUS.success?

  commit.chomp.presence
end

.git ⇒ Pathname

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



52
53
54
# File 'utils/git.rb', line 52

def self.git
  @git ||= T.let(HOMEBREW_SHIMS_PATH/"shared/git", T.nilable(Pathname))
end

.last_revision_commit_of_file(repo, file, before_commit: nil) ⇒ String

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:



74
75
76
77
78
79
80
81
82
# File 'utils/git.rb', line 74

def self.last_revision_commit_of_file(repo, file, before_commit: nil)
  args = if before_commit.nil?
    ["--skip=1"]
  else
    [before_commit.split("..").first]
  end

  Utils.popen_read(git, "-C", repo, "log", "--format=%h", "--abbrev=7", "--max-count=1", *args, "--", file).chomp
end

.last_revision_commit_of_files(repo, files, before_commit: nil) ⇒ Array<([String, nil], Array<String>)>

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'utils/git.rb', line 91

def self.last_revision_commit_of_files(repo, files, before_commit: nil)
  args = if before_commit.nil?
    ["--skip=1"]
  else
    [before_commit.split("..").first]
  end

  # git log output format:
  #   <commit_hash>
  #   <file_path1>
  #   <file_path2>
  #   ...
  # return [<commit_hash>, [file_path1, file_path2, ...]]
  rev, *paths = Utils.popen_read(
    git, "-C", repo, "log",
    "--pretty=format:%h", "--abbrev=7", "--max-count=1",
    "--diff-filter=d", "--name-only", *args, "--", *files
  ).lines.map(&:chomp).reject(&:empty?)
  [rev, paths]
end

.last_revision_of_file(repo, file, before_commit: nil) ⇒ String

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:



116
117
118
119
120
# File 'utils/git.rb', line 116

def self.last_revision_of_file(repo, file, before_commit: nil)
  relative_file = Pathname(file).relative_path_from(repo)
  commit_hash = last_revision_commit_of_file(repo, relative_file, before_commit:)
  file_at_commit(repo, file, commit_hash)
end

.no_global_config_env ⇒ Hash{String => String}

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



20
21
22
# File 'utils/git.rb', line 20

def self.no_global_config_env
  { "GIT_CONFIG_GLOBAL" => no_global_config_file }
end

.no_global_config_file ⇒ String

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



25
26
27
# File 'utils/git.rb', line 25

def self.no_global_config_file
  File::NULL
end

.path ⇒ String?

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



44
45
46
47
48
49
# File 'utils/git.rb', line 44

def self.path
  return unless available?
  return @path if defined?(@path)

  @path = T.let(Utils.popen_read(git, "--homebrew=print-path").chomp.presence, T.nilable(String))
end

.remote_exists?(url) ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:

  • (Boolean)


57
58
59
60
61
# File 'utils/git.rb', line 57

def self.remote_exists?(url)
  return true unless available?

  SystemCommand.quiet_system "git", "ls-remote", "--end-of-options", url
end

.set_name_email!(author: true, committer: true) ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.

Parameters:

  • author (Boolean) (defaults to: true)
  • committer (Boolean) (defaults to: true)


191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'utils/git.rb', line 191

def self.set_name_email!(author: true, committer: true)
  if Homebrew::EnvConfig.git_name
    ENV["GIT_AUTHOR_NAME"] = Homebrew::EnvConfig.git_name if author
    ENV["GIT_COMMITTER_NAME"] = Homebrew::EnvConfig.git_name if committer
  end

  if Homebrew::EnvConfig.git_committer_name && committer
    ENV["GIT_COMMITTER_NAME"] = Homebrew::EnvConfig.git_committer_name
  end

  if Homebrew::EnvConfig.git_email
    ENV["GIT_AUTHOR_EMAIL"] = Homebrew::EnvConfig.git_email if author
    ENV["GIT_COMMITTER_EMAIL"] = Homebrew::EnvConfig.git_email if committer
  end

  return unless committer
  return unless Homebrew::EnvConfig.git_committer_email

  ENV["GIT_COMMITTER_EMAIL"] = Homebrew::EnvConfig.git_committer_email
end

.setup_gpg! ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.



213
214
215
216
217
218
# File 'utils/git.rb', line 213

def self.setup_gpg!
  gnupg_bin = Utils::Path.formula_opt_bin("gnupg")
  return unless gnupg_bin.directory?

  ENV["PATH"] = PATH.new(ENV.fetch("PATH")).prepend(gnupg_bin).to_s
end

.supports_partial_clone_sparse_checkout? ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:

  • (Boolean)


236
237
238
239
240
# File 'utils/git.rb', line 236

def self.supports_partial_clone_sparse_checkout?
  # There is some support for partial clones prior to 2.20, but we avoid using it
  # due to performance issues
  version >= Version.new("2.20.0")
end

.version ⇒ Version

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



30
31
32
33
34
35
36
37
38
39
40
41
# File 'utils/git.rb', line 30

def self.version
  @version ||= T.let(begin
    stdout, _, status = system_command(git, args: ["--version"], env: no_global_config_env,
                                            verbose: false, print_stderr: false).to_a
    version_str = stdout.chomp[/git version (\d+(?:\.\d+)*)/, 1] if status.success?
    if version_str.nil?
      require "utils/tool"
      Utils::Tool.invalidate_cache(:git)
    end
    version_str.nil? ? Version::NULL : Version.new(version_str)
  end, T.nilable(Version))
end