bundler 2.2.2 → 2.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +67 -2
  3. data/bundler.gemspec +1 -1
  4. data/lib/bundler.rb +3 -7
  5. data/lib/bundler/build_metadata.rb +2 -2
  6. data/lib/bundler/cli.rb +2 -5
  7. data/lib/bundler/cli/cache.rb +1 -0
  8. data/lib/bundler/cli/gem.rb +2 -0
  9. data/lib/bundler/cli/update.rb +1 -1
  10. data/lib/bundler/compact_index_client/cache.rb +5 -13
  11. data/lib/bundler/compact_index_client/gem_parser.rb +28 -0
  12. data/lib/bundler/compact_index_client/updater.rb +0 -8
  13. data/lib/bundler/definition.rb +29 -21
  14. data/lib/bundler/dep_proxy.rb +15 -8
  15. data/lib/bundler/feature_flag.rb +0 -1
  16. data/lib/bundler/fetcher.rb +0 -1
  17. data/lib/bundler/gem_helper.rb +9 -7
  18. data/lib/bundler/gem_helpers.rb +30 -24
  19. data/lib/bundler/gem_version_promoter.rb +2 -2
  20. data/lib/bundler/installer.rb +0 -17
  21. data/lib/bundler/installer/standalone.rb +14 -0
  22. data/lib/bundler/lazy_specification.rb +10 -11
  23. data/{man → lib/bundler/man}/bundle-add.1 +1 -1
  24. data/{man → lib/bundler/man}/bundle-binstubs.1 +1 -1
  25. data/{man → lib/bundler/man}/bundle-cache.1 +1 -1
  26. data/{man → lib/bundler/man}/bundle-check.1 +1 -1
  27. data/{man → lib/bundler/man}/bundle-clean.1 +1 -1
  28. data/{man → lib/bundler/man}/bundle-config.1 +4 -4
  29. data/lib/bundler/man/bundle-config.1.ronn +3 -3
  30. data/{man → lib/bundler/man}/bundle-doctor.1 +1 -1
  31. data/{man → lib/bundler/man}/bundle-exec.1 +1 -1
  32. data/{man → lib/bundler/man}/bundle-gem.1 +1 -1
  33. data/{man → lib/bundler/man}/bundle-info.1 +1 -1
  34. data/{man → lib/bundler/man}/bundle-init.1 +1 -1
  35. data/{man → lib/bundler/man}/bundle-inject.1 +1 -1
  36. data/{man → lib/bundler/man}/bundle-install.1 +1 -1
  37. data/{man → lib/bundler/man}/bundle-list.1 +1 -1
  38. data/{man → lib/bundler/man}/bundle-lock.1 +1 -1
  39. data/{man → lib/bundler/man}/bundle-open.1 +1 -1
  40. data/{man → lib/bundler/man}/bundle-outdated.1 +1 -1
  41. data/{man → lib/bundler/man}/bundle-platform.1 +1 -1
  42. data/{man → lib/bundler/man}/bundle-pristine.1 +1 -1
  43. data/{man → lib/bundler/man}/bundle-remove.1 +1 -1
  44. data/{man → lib/bundler/man}/bundle-show.1 +1 -1
  45. data/{man → lib/bundler/man}/bundle-update.1 +1 -1
  46. data/{man → lib/bundler/man}/bundle-viz.1 +1 -1
  47. data/{man → lib/bundler/man}/bundle.1 +1 -1
  48. data/{man → lib/bundler/man}/gemfile.5 +1 -1
  49. data/{man → lib/bundler/man}/index.txt +0 -0
  50. data/lib/bundler/resolver.rb +36 -16
  51. data/lib/bundler/resolver/spec_group.rb +18 -23
  52. data/lib/bundler/rubygems_ext.rb +16 -0
  53. data/lib/bundler/rubygems_integration.rb +0 -5
  54. data/lib/bundler/settings.rb +1 -1
  55. data/lib/bundler/source/git.rb +19 -17
  56. data/lib/bundler/source/git/git_proxy.rb +54 -49
  57. data/lib/bundler/source/path/installer.rb +2 -0
  58. data/lib/bundler/source/rubygems.rb +10 -2
  59. data/lib/bundler/spec_set.rb +6 -9
  60. data/lib/bundler/templates/newgem/Gemfile.tt +1 -1
  61. data/lib/bundler/templates/newgem/newgem.gemspec.tt +1 -1
  62. data/lib/bundler/templates/newgem/rubocop.yml.tt +3 -0
  63. data/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb +7 -0
  64. data/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb +1 -1
  65. data/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb +11 -0
  66. data/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb +11 -7
  67. data/lib/bundler/version.rb +1 -1
  68. metadata +30 -29
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "BUNDLE\-PRISTINE" "1" "November 2020" "" ""
4
+ .TH "BUNDLE\-PRISTINE" "1" "January 2021" "" ""
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBbundle\-pristine\fR \- Restores installed gems to their pristine condition
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "BUNDLE\-REMOVE" "1" "November 2020" "" ""
4
+ .TH "BUNDLE\-REMOVE" "1" "January 2021" "" ""
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBbundle\-remove\fR \- Removes gems from the Gemfile
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "BUNDLE\-SHOW" "1" "November 2020" "" ""
4
+ .TH "BUNDLE\-SHOW" "1" "January 2021" "" ""
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBbundle\-show\fR \- Shows all the gems in your bundle, or the path to a gem
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "BUNDLE\-UPDATE" "1" "November 2020" "" ""
4
+ .TH "BUNDLE\-UPDATE" "1" "January 2021" "" ""
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBbundle\-update\fR \- Update your gems to the latest available versions
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "BUNDLE\-VIZ" "1" "November 2020" "" ""
4
+ .TH "BUNDLE\-VIZ" "1" "January 2021" "" ""
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBbundle\-viz\fR \- Generates a visual dependency graph for your Gemfile
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "BUNDLE" "1" "November 2020" "" ""
4
+ .TH "BUNDLE" "1" "January 2021" "" ""
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBbundle\fR \- Ruby Dependency Management
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "GEMFILE" "5" "November 2020" "" ""
4
+ .TH "GEMFILE" "5" "January 2021" "" ""
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBGemfile\fR \- A format for describing gem dependencies for Ruby programs
File without changes
@@ -32,12 +32,11 @@ module Bundler
32
32
  @base_dg = Molinillo::DependencyGraph.new
33
33
  @base.each do |ls|
34
34
  dep = Dependency.new(ls.name, ls.version)
35
- @base_dg.add_vertex(ls.name, DepProxy.new(dep, ls.platform), true)
35
+ @base_dg.add_vertex(ls.name, DepProxy.get_proxy(dep, ls.platform), true)
36
36
  end
37
37
  additional_base_requirements.each {|d| @base_dg.add_vertex(d.name, d) }
38
38
  @platforms = platforms
39
39
  @gem_version_promoter = gem_version_promoter
40
- @allow_bundler_dependency_conflicts = Bundler.feature_flag.allow_bundler_dependency_conflicts?
41
40
  @use_gvp = Bundler.feature_flag.use_gem_version_promoter_for_major_updates? || !@gem_version_promoter.major?
42
41
  @lockfile_uses_separate_rubygems_sources = Bundler.feature_flag.disable_multisource?
43
42
  end
@@ -75,7 +74,7 @@ module Bundler
75
74
  return unless debug?
76
75
  debug_info = yield
77
76
  debug_info = debug_info.inspect unless debug_info.is_a?(String)
78
- puts debug_info.split("\n").map {|s| "BUNDLER: " + " " * depth + s }
77
+ puts debug_info.split("\n").map {|s| depth == 0 ? "BUNDLER: #{s}" : "BUNDLER(#{depth}): #{s}" }
79
78
  end
80
79
 
81
80
  def debug?
@@ -138,7 +137,6 @@ module Bundler
138
137
  nested.reduce([]) do |groups, (version, specs)|
139
138
  next groups if locked_requirement && !locked_requirement.satisfied_by?(version)
140
139
  spec_group = SpecGroup.new(specs)
141
- spec_group.ignores_bundler_dependencies = @allow_bundler_dependency_conflicts
142
140
  groups << spec_group
143
141
  end
144
142
  else
@@ -163,7 +161,9 @@ module Bundler
163
161
  # Add a spec group for "non platform specific spec" as the fallback
164
162
  # spec group.
165
163
  sg_ruby = sg.copy_for([Gem::Platform::RUBY])
166
- selected_sgs.insert(-2, sg_ruby) if sg_ruby
164
+ next unless sg_ruby
165
+
166
+ selected_sgs.insert(-2, sg_ruby)
167
167
  end
168
168
  selected_sgs
169
169
  end
@@ -206,6 +206,10 @@ module Bundler
206
206
  requirement.matches_spec?(spec) || spec.source.is_a?(Source::Gemspec)
207
207
  end
208
208
 
209
+ def dependencies_equal?(dependencies, other_dependencies)
210
+ dependencies.map(&:dep) == other_dependencies.map(&:dep)
211
+ end
212
+
209
213
  def relevant_sources_for_vertex(vertex)
210
214
  if vertex.root?
211
215
  [@source_requirements[vertex.name]]
@@ -324,10 +328,16 @@ module Bundler
324
328
  def version_conflict_message(e)
325
329
  # only show essential conflicts, if possible
326
330
  conflicts = e.conflicts.dup
327
- conflicts.delete_if do |_name, conflict|
328
- deps = conflict.requirement_trees.map(&:last).flatten(1)
329
- !Bundler::VersionRanges.empty?(*Bundler::VersionRanges.for_many(deps.map(&:requirement)))
331
+
332
+ if conflicts["bundler"]
333
+ conflicts.replace("bundler" => conflicts["bundler"])
334
+ else
335
+ conflicts.delete_if do |_name, conflict|
336
+ deps = conflict.requirement_trees.map(&:last).flatten(1)
337
+ !Bundler::VersionRanges.empty?(*Bundler::VersionRanges.for_many(deps.map(&:requirement)))
338
+ end
330
339
  end
340
+
331
341
  e = Molinillo::VersionConflict.new(conflicts, e.specification_provider) unless conflicts.empty?
332
342
 
333
343
  solver_name = "Bundler"
@@ -355,15 +365,25 @@ module Bundler
355
365
  :additional_message_for_conflict => lambda do |o, name, conflict|
356
366
  if name == "bundler"
357
367
  o << %(\n Current Bundler version:\n bundler (#{Bundler::VERSION}))
358
- other_bundler_required = !conflict.requirement.requirement.satisfied_by?(Gem::Version.new(Bundler::VERSION))
359
- end
360
368
 
361
- if name == "bundler" && other_bundler_required
362
- o << "\n"
363
- o << "This Gemfile requires a different version of Bundler.\n"
364
- o << "Perhaps you need to update Bundler by running `gem install bundler`?\n"
365
- end
366
- if conflict.locked_requirement
369
+ conflict_dependency = conflict.requirement
370
+ conflict_requirement = conflict_dependency.requirement
371
+ other_bundler_required = !conflict_requirement.satisfied_by?(Gem::Version.new(Bundler::VERSION))
372
+
373
+ if other_bundler_required
374
+ o << "\n\n"
375
+
376
+ candidate_specs = @source_requirements[:default_bundler].specs.search(conflict_dependency)
377
+ if candidate_specs.any?
378
+ target_version = candidate_specs.last.version
379
+ new_command = [File.basename($PROGRAM_NAME), "_#{target_version}_", *ARGV].join(" ")
380
+ o << "Your bundle requires a different version of Bundler than the one you're running.\n"
381
+ o << "Install the necessary version with `gem install bundler:#{target_version}` and rerun bundler using `#{new_command}`\n"
382
+ else
383
+ o << "Your bundle requires a different version of Bundler than the one you're running, and that version could not be found.\n"
384
+ end
385
+ end
386
+ elsif conflict.locked_requirement
367
387
  o << "\n"
368
388
  o << %(Running `bundle update` will rebuild your snapshot from scratch, using only\n)
369
389
  o << %(the gems in your Gemfile, which may resolve the conflict.\n)
@@ -6,7 +6,7 @@ module Bundler
6
6
  include GemHelpers
7
7
 
8
8
  attr_accessor :name, :version, :source
9
- attr_accessor :ignores_bundler_dependencies, :activated_platforms
9
+ attr_accessor :activated_platforms
10
10
 
11
11
  def initialize(all_specs)
12
12
  @all_specs = all_specs
@@ -20,16 +20,19 @@ module Bundler
20
20
  @specs = Hash.new do |specs, platform|
21
21
  specs[platform] = select_best_platform_match(all_specs, platform)
22
22
  end
23
- @ignores_bundler_dependencies = true
24
23
  end
25
24
 
26
25
  def to_specs
27
26
  @activated_platforms.map do |p|
28
- next unless s = @specs[p]
29
- lazy_spec = LazySpecification.new(name, version, s.platform, source)
30
- lazy_spec.dependencies.replace s.dependencies
31
- lazy_spec
32
- end.compact.uniq
27
+ specs = @specs[p]
28
+ next unless specs.any?
29
+
30
+ specs.map do |s|
31
+ lazy_spec = LazySpecification.new(name, version, s.platform, source)
32
+ lazy_spec.dependencies.replace s.dependencies
33
+ lazy_spec
34
+ end
35
+ end.flatten.compact.uniq
33
36
  end
34
37
 
35
38
  def copy_for(platforms)
@@ -37,17 +40,12 @@ module Bundler
37
40
  return unless platforms.any?
38
41
 
39
42
  copied_sg = self.class.new(@all_specs)
40
- copied_sg.ignores_bundler_dependencies = @ignores_bundler_dependencies
41
43
  copied_sg.activated_platforms = platforms
42
44
  copied_sg
43
45
  end
44
46
 
45
- def spec_for(platform)
46
- @specs[platform]
47
- end
48
-
49
47
  def for?(platform)
50
- !spec_for(platform).nil?
48
+ @specs[platform].any?
51
49
  end
52
50
 
53
51
  def to_s
@@ -58,7 +56,7 @@ module Bundler
58
56
  def dependencies_for_activated_platforms
59
57
  dependencies = @activated_platforms.map {|p| __dependencies[p] }
60
58
  metadata_dependencies = @activated_platforms.map do |platform|
61
- metadata_dependencies(@specs[platform], platform)
59
+ metadata_dependencies(@specs[platform].first, platform)
62
60
  end
63
61
  dependencies.concat(metadata_dependencies).flatten
64
62
  end
@@ -94,11 +92,11 @@ module Bundler
94
92
  def __dependencies
95
93
  @dependencies = Hash.new do |dependencies, platform|
96
94
  dependencies[platform] = []
97
- if spec = @specs[platform]
95
+ specs = @specs[platform]
96
+ if spec = specs.first
98
97
  spec.dependencies.each do |dep|
99
98
  next if dep.type == :development
100
- next if @ignores_bundler_dependencies && dep.name == "bundler".freeze
101
- dependencies[platform] << DepProxy.new(dep, platform)
99
+ dependencies[platform] << DepProxy.get_proxy(dep, platform)
102
100
  end
103
101
  end
104
102
  dependencies[platform]
@@ -106,16 +104,13 @@ module Bundler
106
104
  end
107
105
 
108
106
  def metadata_dependencies(spec, platform)
109
- return [] unless spec
110
- # Only allow endpoint specifications since they won't hit the network to
111
- # fetch the full gemspec when calling required_ruby_version
112
- return [] if !spec.is_a?(EndpointSpecification) && !spec.is_a?(Gem::Specification)
107
+ return [] unless spec && spec.is_a?(Gem::Specification)
113
108
  dependencies = []
114
109
  if !spec.required_ruby_version.nil? && !spec.required_ruby_version.none?
115
- dependencies << DepProxy.new(Gem::Dependency.new("Ruby\0", spec.required_ruby_version), platform)
110
+ dependencies << DepProxy.get_proxy(Gem::Dependency.new("Ruby\0", spec.required_ruby_version), platform)
116
111
  end
117
112
  if !spec.required_rubygems_version.nil? && !spec.required_rubygems_version.none?
118
- dependencies << DepProxy.new(Gem::Dependency.new("RubyGems\0", spec.required_rubygems_version), platform)
113
+ dependencies << DepProxy.get_proxy(Gem::Dependency.new("RubyGems\0", spec.required_rubygems_version), platform)
119
114
  end
120
115
  dependencies
121
116
  end
@@ -158,6 +158,22 @@ module Gem
158
158
  end
159
159
  end
160
160
 
161
+ if Gem::Requirement.new("~> 2.0").hash == Gem::Requirement.new("~> 2.0.0").hash
162
+ class Requirement
163
+ module CorrectHashForLambdaOperator
164
+ def hash
165
+ if requirements.any? {|r| r.first == "~>" }
166
+ requirements.map {|r| r.first == "~>" ? [r[0], r[1].to_s] : r }.sort.hash
167
+ else
168
+ super
169
+ end
170
+ end
171
+ end
172
+
173
+ prepend CorrectHashForLambdaOperator
174
+ end
175
+ end
176
+
161
177
  class Platform
162
178
  JAVA = Gem::Platform.new("java") unless defined?(JAVA)
163
179
  MSWIN = Gem::Platform.new("mswin32") unless defined?(MSWIN)
@@ -110,11 +110,6 @@ module Bundler
110
110
  obj.to_s
111
111
  end
112
112
 
113
- def platforms
114
- return [Gem::Platform::RUBY] if Bundler.settings[:force_ruby_platform]
115
- Gem.platforms
116
- end
117
-
118
113
  def configuration
119
114
  require_relative "psyched_yaml"
120
115
  Gem.configuration
@@ -7,7 +7,6 @@ module Bundler
7
7
  autoload :Validator, File.expand_path("settings/validator", __dir__)
8
8
 
9
9
  BOOL_KEYS = %w[
10
- allow_bundler_dependency_conflicts
11
10
  allow_deployment_source_credential_changes
12
11
  allow_offline_install
13
12
  auto_clean_without_path
@@ -20,6 +19,7 @@ module Bundler
20
19
  disable_checksum_validation
21
20
  disable_exec_load
22
21
  disable_local_branch_check
22
+ disable_local_revision_check
23
23
  disable_multisource
24
24
  disable_shared_gems
25
25
  disable_version_check
@@ -22,7 +22,7 @@ module Bundler
22
22
  @uri = options["uri"] || ""
23
23
  @safe_uri = URICredentialsFilter.credential_filtered_uri(@uri)
24
24
  @branch = options["branch"]
25
- @ref = options["ref"] || options["branch"] || options["tag"] || "master"
25
+ @ref = options["ref"] || options["branch"] || options["tag"]
26
26
  @submodules = options["submodules"]
27
27
  @name = options["name"]
28
28
  @version = options["version"].to_s.strip.gsub("-", ".pre.")
@@ -60,25 +60,27 @@ module Bundler
60
60
  alias_method :==, :eql?
61
61
 
62
62
  def to_s
63
- at = if local?
64
- path
65
- elsif user_ref = options["ref"]
66
- if ref =~ /\A[a-z0-9]{4,}\z/i
67
- shortref_for_display(user_ref)
63
+ begin
64
+ at = if local?
65
+ path
66
+ elsif user_ref = options["ref"]
67
+ if ref =~ /\A[a-z0-9]{4,}\z/i
68
+ shortref_for_display(user_ref)
69
+ else
70
+ user_ref
71
+ end
72
+ elsif ref
73
+ ref
68
74
  else
69
- user_ref
75
+ git_proxy.branch
70
76
  end
71
- else
72
- ref
73
- end
74
77
 
75
- rev = begin
76
- "@#{shortref_for_display(revision)}"
77
- rescue GitError
78
- nil
79
- end
78
+ rev = " (at #{at}@#{shortref_for_display(revision)})"
79
+ rescue GitError
80
+ ""
81
+ end
80
82
 
81
- "#{@safe_uri} (at #{at}#{rev})"
83
+ "#{@safe_uri}#{rev}"
82
84
  end
83
85
 
84
86
  def name
@@ -146,7 +148,7 @@ module Bundler
146
148
 
147
149
  changed = cached_revision && cached_revision != git_proxy.revision
148
150
 
149
- if changed && !@unlocked && !git_proxy.contains?(cached_revision)
151
+ if !Bundler.settings[:disable_local_revision_check] && changed && !@unlocked && !git_proxy.contains?(cached_revision)
150
152
  raise GitError, "The Gemfile lock is pointing to revision #{shortref_for_display(cached_revision)} " \
151
153
  "but the current branch in your local override for #{name} does not contain such commit. " \
152
154
  "Please make sure your branch is up to date."
@@ -17,7 +17,7 @@ module Bundler
17
17
  class GitNotAllowedError < GitError
18
18
  def initialize(command)
19
19
  msg = String.new
20
- msg << "Bundler is trying to run a `git #{command}` at runtime. You probably need to run `bundle install`. However, "
20
+ msg << "Bundler is trying to run `#{command}` at runtime. You probably need to run `bundle install`. However, "
21
21
  msg << "this error message could probably be more useful. Please submit a ticket at https://github.com/rubygems/rubygems/issues/new?labels=Bundler&template=bundler-related-issue.md "
22
22
  msg << "with steps to reproduce as well as the following\n\nCALLER: #{caller.join("\n")}"
23
23
  super msg
@@ -27,11 +27,11 @@ module Bundler
27
27
  class GitCommandError < GitError
28
28
  attr_reader :command
29
29
 
30
- def initialize(command, path, destination_path, extra_info = nil)
30
+ def initialize(command, path, extra_info = nil)
31
31
  @command = command
32
32
 
33
33
  msg = String.new
34
- msg << "Git error: command `git #{command}` in directory #{destination_path} has failed."
34
+ msg << "Git error: command `#{command}` in directory #{path} has failed."
35
35
  msg << "\n#{extra_info}" if extra_info
36
36
  msg << "\nIf this error persists you could try removing the cache directory '#{path}'" if path.exist?
37
37
  super msg
@@ -39,9 +39,9 @@ module Bundler
39
39
  end
40
40
 
41
41
  class MissingGitRevisionError < GitCommandError
42
- def initialize(command, path, destination_path, ref, repo)
42
+ def initialize(command, destination_path, ref, repo)
43
43
  msg = "Revision #{ref} does not exist in the repository #{repo}. Maybe you misspelled it?"
44
- super command, path, destination_path, msg
44
+ super command, destination_path, msg
45
45
  end
46
46
  end
47
47
 
@@ -67,13 +67,13 @@ module Bundler
67
67
 
68
68
  def branch
69
69
  @branch ||= allowed_with_path do
70
- git("rev-parse --abbrev-ref HEAD", :dir => path).strip
70
+ git("rev-parse", "--abbrev-ref", "HEAD", :dir => path).strip
71
71
  end
72
72
  end
73
73
 
74
74
  def contains?(commit)
75
75
  allowed_with_path do
76
- result, status = git_null("branch --contains #{commit}", :dir => path)
76
+ result, status = git_null("branch", "--contains", commit, :dir => path)
77
77
  status.success? && result =~ /^\* (.*)$/
78
78
  end
79
79
  end
@@ -88,20 +88,22 @@ module Bundler
88
88
 
89
89
  def checkout
90
90
  return if path.exist? && has_revision_cached?
91
- extra_ref = "#{Shellwords.shellescape(ref)}:#{Shellwords.shellescape(ref)}" if ref && ref.start_with?("refs/")
91
+ extra_ref = "#{ref}:#{ref}" if ref && ref.start_with?("refs/")
92
92
 
93
93
  Bundler.ui.info "Fetching #{URICredentialsFilter.credential_filtered_uri(uri)}"
94
94
 
95
+ configured_uri = configured_uri_for(uri).to_s
96
+
95
97
  unless path.exist?
96
98
  SharedHelpers.filesystem_access(path.dirname) do |p|
97
99
  FileUtils.mkdir_p(p)
98
100
  end
99
- git_retry %(clone #{uri_escaped_with_configured_credentials} "#{path}" --bare --no-hardlinks --quiet)
101
+ git_retry "clone", configured_uri, path.to_s, "--bare", "--no-hardlinks", "--quiet"
100
102
  return unless extra_ref
101
103
  end
102
104
 
103
105
  with_path do
104
- git_retry %(fetch --force --quiet --tags #{uri_escaped_with_configured_credentials} "refs/heads/*:refs/heads/*" #{extra_ref}), :dir => path
106
+ git_retry(*["fetch", "--force", "--quiet", "--tags", configured_uri, "refs/heads/*:refs/heads/*", extra_ref].compact, :dir => path)
105
107
  end
106
108
  end
107
109
 
@@ -115,7 +117,7 @@ module Bundler
115
117
  SharedHelpers.filesystem_access(destination) do |p|
116
118
  FileUtils.rm_rf(p)
117
119
  end
118
- git_retry %(clone --no-checkout --quiet "#{path}" "#{destination}")
120
+ git_retry "clone", "--no-checkout", "--quiet", path.to_s, destination.to_s
119
121
  File.chmod(((File.stat(destination).mode | 0o777) & ~File.umask), destination)
120
122
  rescue Errno::EEXIST => e
121
123
  file_path = e.message[%r{.*?((?:[a-zA-Z]:)?/.*)}, 1]
@@ -125,56 +127,59 @@ module Bundler
125
127
  end
126
128
  end
127
129
  # method 2
128
- git_retry %(fetch --force --quiet --tags "#{path}"), :dir => destination
130
+ git_retry "fetch", "--force", "--quiet", "--tags", path.to_s, :dir => destination
129
131
 
130
132
  begin
131
- git "reset --hard #{@revision}", :dir => destination
133
+ git "reset", "--hard", @revision, :dir => destination
132
134
  rescue GitCommandError => e
133
- raise MissingGitRevisionError.new(e.command, path, destination, @revision, URICredentialsFilter.credential_filtered_uri(uri))
135
+ raise MissingGitRevisionError.new(e.command, destination, @revision, URICredentialsFilter.credential_filtered_uri(uri))
134
136
  end
135
137
 
136
138
  if submodules
137
- git_retry "submodule update --init --recursive", :dir => destination
139
+ git_retry "submodule", "update", "--init", "--recursive", :dir => destination
138
140
  elsif Gem::Version.create(version) >= Gem::Version.create("2.9.0")
139
141
  inner_command = "git -C $toplevel submodule deinit --force $sm_path"
140
- inner_command = inner_command.gsub("$") { '\$' } unless Bundler::WINDOWS
141
- git_retry "submodule foreach --quiet \"#{inner_command}\"", :dir => destination
142
+ git_retry "submodule", "foreach", "--quiet", inner_command, :dir => destination
142
143
  end
143
144
  end
144
145
 
145
146
  private
146
147
 
147
- def git_null(command, dir: SharedHelpers.pwd)
148
+ def git_null(*command, dir: nil)
148
149
  check_allowed(command)
149
150
 
150
151
  out, status = SharedHelpers.with_clean_git_env do
151
- capture_and_ignore_stderr("git #{command}", :chdir => dir.to_s)
152
+ capture_and_ignore_stderr(*capture3_args_for(command, dir))
152
153
  end
153
154
 
154
155
  [URICredentialsFilter.credential_filtered_string(out, uri), status]
155
156
  end
156
157
 
157
- def git_retry(command, dir: SharedHelpers.pwd)
158
- Bundler::Retry.new("`git #{URICredentialsFilter.credential_filtered_string(command, uri)}`", GitNotAllowedError).attempts do
159
- git(command, :dir => dir)
158
+ def git_retry(*command, dir: nil)
159
+ command_with_no_credentials = check_allowed(command)
160
+
161
+ Bundler::Retry.new("`#{command_with_no_credentials}` at #{dir || SharedHelpers.pwd}").attempts do
162
+ git(*command, :dir => dir)
160
163
  end
161
164
  end
162
165
 
163
- def git(command, dir: SharedHelpers.pwd)
166
+ def git(*command, dir: nil)
164
167
  command_with_no_credentials = check_allowed(command)
165
168
 
166
169
  out, status = SharedHelpers.with_clean_git_env do
167
- capture_and_filter_stderr(uri, "git #{command}", :chdir => dir.to_s)
170
+ capture_and_filter_stderr(*capture3_args_for(command, dir))
168
171
  end
169
172
 
170
- raise GitCommandError.new(command_with_no_credentials, path, dir) unless status.success?
173
+ filtered_out = URICredentialsFilter.credential_filtered_string(out, uri)
174
+
175
+ raise GitCommandError.new(command_with_no_credentials, dir || SharedHelpers.pwd, filtered_out) unless status.success?
171
176
 
172
- URICredentialsFilter.credential_filtered_string(out, uri)
177
+ filtered_out
173
178
  end
174
179
 
175
180
  def has_revision_cached?
176
181
  return unless @revision
177
- with_path { git("cat-file -e #{@revision}", :dir => path) }
182
+ with_path { git("cat-file", "-e", @revision, :dir => path) }
178
183
  true
179
184
  rescue GitError
180
185
  false
@@ -186,24 +191,10 @@ module Bundler
186
191
 
187
192
  def find_local_revision
188
193
  allowed_with_path do
189
- git("rev-parse --verify #{Shellwords.shellescape(ref)}", :dir => path).strip
194
+ git("rev-parse", "--verify", ref || "HEAD", :dir => path).strip
190
195
  end
191
196
  rescue GitCommandError => e
192
- raise MissingGitRevisionError.new(e.command, path, path, ref, URICredentialsFilter.credential_filtered_uri(uri))
193
- end
194
-
195
- # Escape the URI for git commands
196
- def uri_escaped_with_configured_credentials
197
- remote = configured_uri_for(uri)
198
- if Bundler::WINDOWS
199
- # Windows quoting requires double quotes only, with double quotes
200
- # inside the string escaped by being doubled.
201
- '"' + remote.gsub('"') { '""' } + '"'
202
- else
203
- # Bash requires single quoted strings, with the single quotes escaped
204
- # by ending the string, escaping the quote, and restarting the string.
205
- "'" + remote.gsub("'") { "'\\''" } + "'"
206
- end
197
+ raise MissingGitRevisionError.new(e.command, path, ref, URICredentialsFilter.credential_filtered_uri(uri))
207
198
  end
208
199
 
209
200
  # Adds credentials to the URI as Fetcher#configured_uri_for does
@@ -233,23 +224,37 @@ module Bundler
233
224
  end
234
225
 
235
226
  def check_allowed(command)
236
- command_with_no_credentials = URICredentialsFilter.credential_filtered_string(command, uri)
227
+ command_with_no_credentials = URICredentialsFilter.credential_filtered_string("git #{command.shelljoin}", uri)
237
228
  raise GitNotAllowedError.new(command_with_no_credentials) unless allow?
238
229
  command_with_no_credentials
239
230
  end
240
231
 
241
- def capture_and_filter_stderr(uri, cmd, chdir: SharedHelpers.pwd)
232
+ def capture_and_filter_stderr(*cmd)
242
233
  require "open3"
243
- return_value, captured_err, status = Open3.capture3(cmd, :chdir => chdir)
244
- Bundler.ui.warn URICredentialsFilter.credential_filtered_string(captured_err, uri) if uri && !captured_err.empty?
234
+ return_value, captured_err, status = Open3.capture3(*cmd)
235
+ Bundler.ui.warn URICredentialsFilter.credential_filtered_string(captured_err, uri) unless captured_err.empty?
245
236
  [return_value, status]
246
237
  end
247
238
 
248
- def capture_and_ignore_stderr(cmd, chdir: SharedHelpers.pwd)
239
+ def capture_and_ignore_stderr(*cmd)
249
240
  require "open3"
250
- return_value, _, status = Open3.capture3(cmd, :chdir => chdir)
241
+ return_value, _, status = Open3.capture3(*cmd)
251
242
  [return_value, status]
252
243
  end
244
+
245
+ def capture3_args_for(cmd, dir)
246
+ return ["git", *cmd] unless dir
247
+
248
+ if Bundler.feature_flag.bundler_3_mode? || supports_minus_c?
249
+ ["git", "-C", dir.to_s, *cmd]
250
+ else
251
+ ["git", *cmd, { :chdir => dir.to_s }]
252
+ end
253
+ end
254
+
255
+ def supports_minus_c?
256
+ @supports_minus_c ||= Gem::Version.new(version) >= Gem::Version.new("1.8.5")
257
+ end
253
258
  end
254
259
  end
255
260
  end