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:



131
132
133
134
135
# File 'utils/git.rb', line 131

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:



218
219
220
221
222
223
224
225
226
227
228
# File 'utils/git.rb', line 218

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.



60
61
62
63
64
# File 'utils/git.rb', line 60

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.



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'utils/git.rb', line 164

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!
      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:



119
120
121
122
123
# File 'utils/git.rb', line 119

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:



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'utils/git.rb', line 141

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:



48
49
50
# File 'utils/git.rb', line 48

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:



70
71
72
73
74
75
76
77
78
# File 'utils/git.rb', line 70

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:



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'utils/git.rb', line 87

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:



112
113
114
115
116
# File 'utils/git.rb', line 112

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:



40
41
42
43
44
45
# File 'utils/git.rb', line 40

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)


53
54
55
56
57
# File 'utils/git.rb', line 53

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)


186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'utils/git.rb', line 186

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.



208
209
210
211
212
213
# File 'utils/git.rb', line 208

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)


231
232
233
234
235
# File 'utils/git.rb', line 231

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
# 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 = status.success? ? stdout.chomp[/git version (\d+(?:\.\d+)*)/, 1] : nil
    version_str.nil? ? Version::NULL : Version.new(version_str)
  end, T.nilable(Version))
end