Merge pull request #8456 from reitermarkus/document-git

Refactor and document `Git`.
This commit is contained in:
Markus Reiter 2020-08-24 00:03:02 +02:00 committed by GitHub
commit bf25717683
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 155 additions and 156 deletions

View File

@ -54,7 +54,7 @@ class DependencyCollector
end end
def git_dep_if_needed(tags) def git_dep_if_needed(tags)
return if Utils.git_available? return if Utils::Git.available?
Dependency.new("git", tags) Dependency.new("git", tags)
end end

View File

@ -1085,7 +1085,7 @@ module Homebrew
problem http_content_problem problem http_content_problem
end end
elsif strategy <= GitDownloadStrategy elsif strategy <= GitDownloadStrategy
problem "The URL #{url} is not a valid git URL" unless Utils.git_remote_exists? url problem "The URL #{url} is not a valid git URL" unless Utils::Git.remote_exists? url
elsif strategy <= SubversionDownloadStrategy elsif strategy <= SubversionDownloadStrategy
next unless DevelopmentTools.subversion_handles_most_https_certificates? next unless DevelopmentTools.subversion_handles_most_https_certificates?
next unless Utils.svn_available? next unless Utils.svn_available?

View File

@ -550,7 +550,7 @@ module Homebrew
end end
unless args.no_commit? unless args.no_commit?
Utils.set_git_name_email! Utils::Git.set_name_email!
short_name = formula_name.split("/", -1).last short_name = formula_name.split("/", -1).last
pkg_version = bottle_hash["formula"]["pkg_version"] pkg_version = bottle_hash["formula"]["pkg_version"]

View File

@ -133,7 +133,7 @@ module Homebrew
result = "" result = ""
loop do loop do
rev = rev.nil? ? "HEAD" : "#{rev}~1" rev = rev.nil? ? "HEAD" : "#{rev}~1"
rev, (path,) = Git.last_revision_commit_of_files(repo, pattern, before_commit: rev) rev, (path,) = Utils::Git.last_revision_commit_of_files(repo, pattern, before_commit: rev)
if rev.nil? && source_tap.shallow? if rev.nil? && source_tap.shallow?
odie <<~EOS odie <<~EOS
Could not find #{name} but #{source_tap} is a shallow clone! Could not find #{name} but #{source_tap} is a shallow clone!
@ -145,7 +145,7 @@ module Homebrew
end end
file = repo/path file = repo/path
result = Git.last_revision_of_file(repo, file, before_commit: rev) result = Utils::Git.last_revision_of_file(repo, file, before_commit: rev)
if result.empty? if result.empty?
odebug "Skipping revision #{rev} - file is empty at this revision" odebug "Skipping revision #{rev} - file is empty at this revision"
next next
@ -173,11 +173,11 @@ module Homebrew
if files.empty? if files.empty?
ohai "Searching repository history" ohai "Searching repository history"
rev, (path,) = Git.last_revision_commit_of_files(repo, pattern) rev, (path,) = Utils::Git.last_revision_commit_of_files(repo, pattern)
odie "Could not find #{name}! The formula or version may not have existed." if rev.nil? odie "Could not find #{name}! The formula or version may not have existed." if rev.nil?
file = repo/path file = repo/path
version = formula_at_revision(repo, name, file, rev).version version = formula_at_revision(repo, name, file, rev).version
result = Git.last_revision_of_file(repo, file) result = Utils::Git.last_revision_of_file(repo, file)
else else
file = files.first.realpath file = files.first.realpath
rev = "HEAD" rev = "HEAD"
@ -219,7 +219,7 @@ module Homebrew
def formula_at_revision(repo, name, file, rev) def formula_at_revision(repo, name, file, rev)
return if rev.empty? return if rev.empty?
contents = Git.last_revision_of_file(repo, file, before_commit: rev) contents = Utils::Git.last_revision_of_file(repo, file, before_commit: rev)
contents.gsub!("@url=", "url ") contents.gsub!("@url=", "url ")
contents.gsub!("require 'brewkit'", "require 'formula'") contents.gsub!("require 'brewkit'", "require 'formula'")
with_monkey_patch { Formulary.from_contents(name, file, contents) } with_monkey_patch { Formulary.from_contents(name, file, contents) }

View File

@ -50,7 +50,7 @@ module Homebrew
args = pull_args.parse args = pull_args.parse
# Passthrough Git environment variables for e.g. git am # Passthrough Git environment variables for e.g. git am
Utils.set_git_name_email!(author: false, committer: true) Utils::Git.set_name_email!(author: false, committer: true)
# Depending on user configuration, git may try to invoke gpg. # Depending on user configuration, git may try to invoke gpg.
if Utils.popen_read("git config --get --bool commit.gpgsign").chomp == "true" if Utils.popen_read("git config --get --bool commit.gpgsign").chomp == "true"

View File

@ -96,7 +96,7 @@ module Homebrew
end end
def examine_git_origin(repository_path, desired_origin) def examine_git_origin(repository_path, desired_origin)
return if !Utils.git_available? || !repository_path.git? return if !Utils::Git.available? || !repository_path.git?
current_origin = repository_path.git_origin current_origin = repository_path.git_origin
@ -482,13 +482,13 @@ module Homebrew
def check_git_version def check_git_version
minimum_version = ENV["HOMEBREW_MINIMUM_GIT_VERSION"] minimum_version = ENV["HOMEBREW_MINIMUM_GIT_VERSION"]
return unless Utils.git_available? return unless Utils::Git.available?
return if Version.create(Utils.git_version) >= Version.create(minimum_version) return if Version.create(Utils::Git.version) >= Version.create(minimum_version)
git = Formula["git"] git = Formula["git"]
git_upgrade_cmd = git.any_version_installed? ? "upgrade" : "install" git_upgrade_cmd = git.any_version_installed? ? "upgrade" : "install"
<<~EOS <<~EOS
An outdated version (#{Utils.git_version}) of Git was detected in your PATH. An outdated version (#{Utils::Git.version}) of Git was detected in your PATH.
Git #{minimum_version} or newer is required for Homebrew. Git #{minimum_version} or newer is required for Homebrew.
Please upgrade: Please upgrade:
brew #{git_upgrade_cmd} git brew #{git_upgrade_cmd} git
@ -496,7 +496,7 @@ module Homebrew
end end
def check_for_git def check_for_git
return if Utils.git_available? return if Utils::Git.available?
<<~EOS <<~EOS
Git could not be found in your PATH. Git could not be found in your PATH.
@ -507,7 +507,7 @@ module Homebrew
end end
def check_git_newline_settings def check_git_newline_settings
return unless Utils.git_available? return unless Utils::Git.available?
autocrlf = HOMEBREW_REPOSITORY.cd { `git config --get core.autocrlf`.chomp } autocrlf = HOMEBREW_REPOSITORY.cd { `git config --get core.autocrlf`.chomp }
return unless autocrlf == "true" return unless autocrlf == "true"
@ -543,7 +543,7 @@ module Homebrew
return if ENV["CI"] return if ENV["CI"]
coretap_path = CoreTap.instance.path coretap_path = CoreTap.instance.path
return if !Utils.git_available? || !(coretap_path/".git").exist? return if !Utils::Git.available? || !(coretap_path/".git").exist?
branch = coretap_path.git_branch branch = coretap_path.git_branch
return if branch.blank? || branch.include?("master") return if branch.blank? || branch.include?("master")
@ -642,7 +642,7 @@ module Homebrew
end end
def check_git_status def check_git_status
return unless Utils.git_available? return unless Utils::Git.available?
message = nil message = nil

View File

@ -9,43 +9,43 @@ module GitRepositoryExtension
end end
def git_origin def git_origin
return unless git? && Utils.git_available? return unless git? && Utils::Git.available?
Utils.popen_read("git", "config", "--get", "remote.origin.url", chdir: self).chomp.presence Utils.popen_read("git", "config", "--get", "remote.origin.url", chdir: self).chomp.presence
end end
def git_origin=(origin) def git_origin=(origin)
return unless git? && Utils.git_available? return unless git? && Utils::Git.available?
safe_system "git", "remote", "set-url", "origin", origin, chdir: self safe_system "git", "remote", "set-url", "origin", origin, chdir: self
end end
def git_head def git_head
return unless git? && Utils.git_available? return unless git? && Utils::Git.available?
Utils.popen_read("git", "rev-parse", "--verify", "-q", "HEAD", chdir: self).chomp.presence Utils.popen_read("git", "rev-parse", "--verify", "-q", "HEAD", chdir: self).chomp.presence
end end
def git_short_head def git_short_head
return unless git? && Utils.git_available? return unless git? && Utils::Git.available?
Utils.popen_read("git", "rev-parse", "--short=4", "--verify", "-q", "HEAD", chdir: self).chomp.presence Utils.popen_read("git", "rev-parse", "--short=4", "--verify", "-q", "HEAD", chdir: self).chomp.presence
end end
def git_last_commit def git_last_commit
return unless git? && Utils.git_available? return unless git? && Utils::Git.available?
Utils.popen_read("git", "show", "-s", "--format=%cr", "HEAD", chdir: self).chomp.presence Utils.popen_read("git", "show", "-s", "--format=%cr", "HEAD", chdir: self).chomp.presence
end end
def git_branch def git_branch
return unless git? && Utils.git_available? return unless git? && Utils::Git.available?
Utils.popen_read("git", "rev-parse", "--abbrev-ref", "HEAD", chdir: self).chomp.presence Utils.popen_read("git", "rev-parse", "--abbrev-ref", "HEAD", chdir: self).chomp.presence
end end
def git_last_commit_date def git_last_commit_date
return unless git? && Utils.git_available? return unless git? && Utils::Git.available?
Utils.popen_read("git", "show", "-s", "--format=%cd", "--date=short", "HEAD", chdir: self).chomp.presence Utils.popen_read("git", "show", "-s", "--format=%cd", "--date=short", "HEAD", chdir: self).chomp.presence
end end

View File

@ -1821,7 +1821,7 @@ class Formula
test_env[:_JAVA_OPTIONS] += " -Djava.io.tmpdir=#{HOMEBREW_TEMP}" test_env[:_JAVA_OPTIONS] += " -Djava.io.tmpdir=#{HOMEBREW_TEMP}"
ENV.clear_sensitive_environment! ENV.clear_sensitive_environment!
Utils.set_git_name_email! Utils::Git.set_name_email!
mktemp("#{name}-test") do |staging| mktemp("#{name}-test") do |staging|
staging.retain! if keep_tmp staging.retain! if keep_tmp

View File

@ -722,8 +722,8 @@ class FormulaInstaller
tab.runtime_dependencies = Tab.runtime_deps_hash(f_runtime_deps) tab.runtime_dependencies = Tab.runtime_deps_hash(f_runtime_deps)
tab.write tab.write
# let's reset Utils.git_available? if we just installed git # let's reset Utils::Git.available? if we just installed git
Utils.clear_git_available_cache if formula.name == "git" Utils::Git.clear_available_cache if formula.name == "git"
# use installed curl when it's needed and available # use installed curl when it's needed and available
if formula.name == "curl" && if formula.name == "curl" &&

View File

@ -98,9 +98,9 @@ module SystemConfig
end end
def describe_git def describe_git
return "N/A" unless Utils.git_available? return "N/A" unless Utils::Git.available?
"#{Utils.git_version} => #{Utils.git_path}" "#{Utils::Git.version} => #{Utils::Git.path}"
end end
def describe_curl def describe_curl

View File

@ -248,7 +248,7 @@ class Tap
end end
# ensure git is installed # ensure git is installed
Utils.ensure_git_installed! Utils::Git.ensure_installed!
if installed? if installed?
unless force_auto_update.nil? unless force_auto_update.nil?
@ -707,7 +707,7 @@ class TapConfig
def [](key) def [](key)
return unless tap.git? return unless tap.git?
return unless Utils.git_available? return unless Utils::Git.available?
tap.path.cd do tap.path.cd do
Utils.popen_read("git", "config", "--get", "homebrew.#{key}").chomp.presence Utils.popen_read("git", "config", "--get", "homebrew.#{key}").chomp.presence
@ -716,7 +716,7 @@ class TapConfig
def []=(key, value) def []=(key, value)
return unless tap.git? return unless tap.git?
return unless Utils.git_available? return unless Utils::Git.available?
tap.path.cd do tap.path.cd do
safe_system "git", "config", "--replace-all", "homebrew.#{key}", value.to_s safe_system "git", "config", "--replace-all", "homebrew.#{key}", value.to_s

View File

@ -168,7 +168,7 @@ describe Tap do
it "returns nil if Git is not available" do it "returns nil if Git is not available" do
setup_git_repo setup_git_repo
allow(Utils).to receive(:git_available?).and_return(false) allow(Utils::Git).to receive(:available?).and_return(false)
expect(subject.remote).to be nil expect(subject.remote).to be nil
end end
end end

View File

@ -2,8 +2,10 @@
require "utils/git" require "utils/git"
describe Git do describe Utils::Git do
before do before do
described_class.clear_available_cache
git = HOMEBREW_SHIMS_PATH/"scm/git" git = HOMEBREW_SHIMS_PATH/"scm/git"
HOMEBREW_CACHE.cd do HOMEBREW_CACHE.cd do
@ -88,49 +90,43 @@ describe Git do
).to eq("# README") ).to eq("# README")
end end
end end
end
describe Utils do describe "::available?" do
before do
described_class.clear_git_available_cache
end
describe "::git_available?" do
it "returns true if git --version command succeeds" do it "returns true if git --version command succeeds" do
expect(described_class).to be_git_available expect(described_class).to be_available
end end
it "returns false if git --version command does not succeed" do it "returns false if git --version command does not succeed" do
stub_const("HOMEBREW_SHIMS_PATH", HOMEBREW_PREFIX/"bin/shim") stub_const("HOMEBREW_SHIMS_PATH", HOMEBREW_PREFIX/"bin/shim")
expect(described_class).not_to be_git_available expect(described_class).not_to be_available
end end
end end
describe "::git_path" do describe "::path" do
it "returns nil when git is not available" do it "returns nil when git is not available" do
stub_const("HOMEBREW_SHIMS_PATH", HOMEBREW_PREFIX/"bin/shim") stub_const("HOMEBREW_SHIMS_PATH", HOMEBREW_PREFIX/"bin/shim")
expect(described_class.git_path).to eq(nil) expect(described_class.path).to eq(nil)
end end
it "returns path of git when git is available" do it "returns path of git when git is available" do
expect(described_class.git_path).to end_with("git") expect(described_class.path).to end_with("git")
end end
end end
describe "::git_version" do describe "::version" do
it "returns nil when git is not available" do it "returns nil when git is not available" do
stub_const("HOMEBREW_SHIMS_PATH", HOMEBREW_PREFIX/"bin/shim") stub_const("HOMEBREW_SHIMS_PATH", HOMEBREW_PREFIX/"bin/shim")
expect(described_class.git_version).to eq(nil) expect(described_class.version).to eq(nil)
end end
it "returns version of git when git is available" do it "returns version of git when git is available" do
expect(described_class.git_version).not_to be_nil expect(described_class.version).not_to be_nil
end end
end end
describe "::ensure_git_installed!" do describe "::ensure_installed!" do
it "returns nil if git already available" do it "returns nil if git already available" do
expect(described_class.ensure_git_installed!).to be_nil expect(described_class.ensure_installed!).to be_nil
end end
context "when git is not already available" do context "when git is not already available" do
@ -140,25 +136,28 @@ describe Utils do
it "can't install brewed git if homebrew/core is unavailable" do it "can't install brewed git if homebrew/core is unavailable" do
allow_any_instance_of(Pathname).to receive(:directory?).and_return(false) allow_any_instance_of(Pathname).to receive(:directory?).and_return(false)
expect { described_class.ensure_git_installed! }.to raise_error("Git is unavailable") expect { described_class.ensure_installed! }.to raise_error("Git is unavailable")
end end
it "raises error if can't install git" do it "raises error if can't install git" do
stub_const("HOMEBREW_BREW_FILE", HOMEBREW_PREFIX/"bin/brew") stub_const("HOMEBREW_BREW_FILE", HOMEBREW_PREFIX/"bin/brew")
expect { described_class.ensure_git_installed! }.to raise_error("Git is unavailable") expect { described_class.ensure_installed! }.to raise_error("Git is unavailable")
end end
it "installs git" do it "installs git" do
allow(Homebrew).to receive(:_system).with(any_args).and_return(true) expect(described_class).to receive(:available?).and_return(false)
described_class.ensure_git_installed! expect(described_class).to receive(:safe_system).with(HOMEBREW_BREW_FILE, "install", "git").and_return(true)
expect(described_class).to receive(:available?).and_return(true)
described_class.ensure_installed!
end end
end end
end end
describe "::git_remote_exists?" do describe "::remote_exists?" do
it "returns true when git is not available" do it "returns true when git is not available" do
stub_const("HOMEBREW_SHIMS_PATH", HOMEBREW_PREFIX/"bin/shim") stub_const("HOMEBREW_SHIMS_PATH", HOMEBREW_PREFIX/"bin/shim")
expect(described_class).to be_git_remote_exists("blah") expect(described_class).to be_remote_exists("blah")
end end
context "when git is available" do context "when git is available" do
@ -173,11 +172,11 @@ describe Utils do
system git, "remote", "add", "origin", url system git, "remote", "add", "origin", url
end end
expect(described_class).to be_git_remote_exists(url) expect(described_class).to be_remote_exists(url)
end end
it "returns false when git remote does not exist" do it "returns false when git remote does not exist" do
expect(described_class).not_to be_git_remote_exists("blah") expect(described_class).not_to be_remote_exists("blah")
end end
end end
end end

View File

@ -2,116 +2,116 @@
require "open3" require "open3"
module Git
module_function
def last_revision_commit_of_file(repo, file, before_commit: nil)
args = if before_commit.nil?
["--skip=1"]
else
[before_commit.split("..").first]
end
out, = Open3.capture3(
HOMEBREW_SHIMS_PATH/"scm/git", "-C", repo,
"log", "--format=%h", "--abbrev=7", "--max-count=1",
*args, "--", file
)
out.chomp
end
def 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, ...]]
out, = Open3.capture3(
HOMEBREW_SHIMS_PATH/"scm/git", "-C", repo, "log",
"--pretty=format:%h", "--abbrev=7", "--max-count=1",
"--diff-filter=d", "--name-only", *args, "--", *files
)
rev, *paths = out.chomp.split(/\n/).reject(&:empty?)
[rev, paths]
end
def 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: before_commit)
out, = Open3.capture3(
HOMEBREW_SHIMS_PATH/"scm/git", "-C", repo,
"show", "#{commit_hash}:#{relative_file}"
)
out
end
end
module Utils module Utils
def self.git_available? # Helper functions for querying Git information.
@git_available ||= quiet_system HOMEBREW_SHIMS_PATH/"scm/git", "--version" #
end # @api private
module Git
module_function
def self.git_path def available?
return unless git_available? version.present?
end
@git_path ||= Utils.popen_read( def version
HOMEBREW_SHIMS_PATH/"scm/git", "--homebrew=print-path" return @version if defined?(@version)
).chomp.presence
end
def self.git_version stdout, _, status = system_command(HOMEBREW_SHIMS_PATH/"scm/git", args: ["--version"], print_stderr: false)
return unless git_available? @version = status.success? ? stdout.chomp[/git version (\d+(?:\.\d+)*)/, 1] : nil
end
@git_version ||= Utils.popen_read( def path
HOMEBREW_SHIMS_PATH/"scm/git", "--version" return unless available?
).chomp[/git version (\d+(?:\.\d+)*)/, 1] return @path if defined?(@path)
end
def self.ensure_git_installed! @path = Utils.popen_read(HOMEBREW_SHIMS_PATH/"scm/git", "--homebrew=print-path").chomp.presence
return if git_available? end
# we cannot install brewed git if homebrew/core is unavailable. def remote_exists?(url)
if CoreTap.instance.installed? return true unless available?
begin
oh1 "Installing #{Formatter.identifier("git")}" quiet_system "git", "ls-remote", url
safe_system HOMEBREW_BREW_FILE, "install", "git" end
rescue
raise "Git is unavailable" def clear_available_cache
remove_instance_variable(:@version) if defined?(@version)
remove_instance_variable(:@path) if defined?(@path)
end
def last_revision_commit_of_file(repo, file, before_commit: nil)
args = if before_commit.nil?
["--skip=1"]
else
[before_commit.split("..").first]
end end
out, = Open3.capture3(
HOMEBREW_SHIMS_PATH/"scm/git", "-C", repo,
"log", "--format=%h", "--abbrev=7", "--max-count=1",
*args, "--", file
)
out.chomp
end end
raise "Git is unavailable" unless git_available? def last_revision_commit_of_files(repo, files, before_commit: nil)
end args = if before_commit.nil?
["--skip=1"]
else
[before_commit.split("..").first]
end
def self.clear_git_available_cache # git log output format:
@git_available = nil # <commit_hash>
@git_path = nil # <file_path1>
@git_version = nil # <file_path2>
end # ...
# return [<commit_hash>, [file_path1, file_path2, ...]]
def self.git_remote_exists?(url) out, = Open3.capture3(
return true unless git_available? HOMEBREW_SHIMS_PATH/"scm/git", "-C", repo, "log",
"--pretty=format:%h", "--abbrev=7", "--max-count=1",
quiet_system "git", "ls-remote", url "--diff-filter=d", "--name-only", *args, "--", *files
end )
rev, *paths = out.chomp.split(/\n/).reject(&:empty?)
def self.set_git_name_email!(author: true, committer: true) [rev, paths]
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 end
return unless Homebrew::EnvConfig.git_email def last_revision_of_file(repo, file, before_commit: nil)
relative_file = Pathname(file).relative_path_from(repo)
ENV["GIT_AUTHOR_EMAIL"] = Homebrew::EnvConfig.git_email if author commit_hash = last_revision_commit_of_file(repo, relative_file, before_commit: before_commit)
ENV["GIT_COMMITTER_EMAIL"] = Homebrew::EnvConfig.git_email if committer out, = Open3.capture3(
HOMEBREW_SHIMS_PATH/"scm/git", "-C", repo,
"show", "#{commit_hash}:#{relative_file}"
)
out
end
def ensure_installed!
return if available?
# we cannot install brewed git if homebrew/core is unavailable.
if CoreTap.instance.installed?
begin
oh1 "Installing #{Formatter.identifier("git")}"
safe_system HOMEBREW_BREW_FILE, "install", "git"
rescue
raise "Git is unavailable"
end
end
raise "Git is unavailable" unless available?
end
def 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
return unless 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
end end
end end