diff options
-rw-r--r-- | CHANGELOG.md | 8 | ||||
-rw-r--r-- | lib/bundler/resolver.rb | 47 | ||||
-rw-r--r-- | spec/realworld/edgecases_spec.rb | 35 |
3 files changed, 76 insertions, 14 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 13b10aed85..16564d5acb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,14 @@ Features: Documentation: - add missing Gemfile global `path` explanation (@agenteo) +## 1.7.9 + +Bugfixes: + + - Fix an issue where bundler sometime spams one gem in Gemfile.lock (#3216, @Who828) + - Ensure bundle update installs the newer version of the gem (#3089, @Who828) + - Fix an regression which stopped Bundler from resolving some Gemfiles (#3059, #3248, @Who828) + ## 1.7.8 (2014-12-06) Bugfixes: diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index 218a69969d..709ad67a34 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -110,7 +110,7 @@ module Bundler end end - attr_reader :errors, :started_at, :iteration_rate, :iteration_counter + attr_reader :errors, :started_at, :iteration_rate, :iteration_counter, :initial_reqs # Figures out the best possible configuration of gems that satisfies # the list of passed dependencies and any child dependencies without @@ -135,6 +135,7 @@ module Bundler end def initialize(index, source_requirements, base) + @initial_reqs = [] @errors = {} @base = base @index = index @@ -158,11 +159,12 @@ module Bundler activated.values.map { |s| s.to_specs }.flatten.compact end - def start(reqs) + def start(reqs, current_traversal=false) + @initial_reqs = reqs.dup unless current_traversal activated = {} @gems_size = Hash[reqs.map { |r| [r, gems_size(r)] }] - resolve(reqs, activated) + resolve(reqs, activated, current_traversal) end class State < Struct.new(:reqs, :activated, :requirement, :possibles, :depth, :conflicts) @@ -172,13 +174,16 @@ module Bundler end def handle_conflict(current, states, existing=nil) - until current.nil? && existing.nil? + until current.nil? current_state = find_state(current, states) - existing_state = find_state(existing, states) return current if state_any?(current_state) + current = current.required_by.last if current + end + + until existing.nil? + existing_state = find_state(existing, states) return existing if state_any?(existing_state) existing = existing.required_by.last if existing - current = current.required_by.last if current end end @@ -225,7 +230,7 @@ module Bundler end def resolve_for_conflict(state) - raise version_conflict if state.nil? || state.possibles.empty? + return version_conflict if state.nil? || state.possibles.empty? reqs, activated, depth, conflicts = state.reqs.dup, state.activated.dup, state.depth, state.conflicts.dup requirement = state.requirement possible = state.possibles.pop @@ -252,7 +257,7 @@ module Bundler return reqs, activated, depth, conflicts end - def resolve(reqs, activated) + def resolve(reqs, activated, current_traversal) states = [] depth = 0 conflicts = Set.new @@ -311,17 +316,23 @@ module Bundler conflicts << current.name parent = current.required_by.last - if existing.respond_to?(:required_by) - parent = handle_conflict(current, states, existing.required_by[-2]) unless other_possible?(parent, states) + + if current_traversal + parent = handle_conflict(current, states) else - parent = handle_conflict(current, states) unless other_possible?(parent, states) + parent = handle_conflict(parent, states) end + if parent.nil? && !conflicts.empty? parent = states.reverse.detect { |i| conflicts.include?(i.name) && state_any?(i)} end - raise version_conflict if parent.nil? || parent.name == 'bundler' + if existing.respond_to?(:required_by) + parent = handle_conflict(parent, states, existing.required_by[-2]) unless other_possible?(parent, states) + end + + return version_conflict(current_traversal) if parent.nil? || parent.name == 'bundler' reqs, activated, depth, conflicts = resolve_conflict(parent, states) end @@ -365,6 +376,7 @@ module Bundler end end + state = State.new(reqs.dup, activated.dup, current, matching_versions, depth, conflicts) states << state requirement = state.possibles.pop @@ -419,8 +431,15 @@ module Bundler end end - def version_conflict - VersionConflict.new(errors.keys, error_message) + def version_conflict(current_traversal=true) + raise VersionConflict.new(errors.keys, error_message) if current_traversal + reset_state + start(initial_reqs, true) + end + + def reset_state + clear_search_cache + @errors = {} end # For a given conflicted requirement, print out what exactly went wrong diff --git a/spec/realworld/edgecases_spec.rb b/spec/realworld/edgecases_spec.rb index 263ac520b3..000ded4fd0 100644 --- a/spec/realworld/edgecases_spec.rb +++ b/spec/realworld/edgecases_spec.rb @@ -35,6 +35,41 @@ describe "real world edgecases", :realworld => true do expect(out).to include("activemodel 3.0.5") end + it "resolves dependencies correctly", :ruby => "1.9" do + install_gemfile <<-G + source "https://rubygems.org" + + gem 'rails', '~> 3.0' + gem 'capybara', '~> 2.2.0' + G + expect(out).to include("rails 3.2.21") + expect(out).to include("capybara 2.2.1") + end + + it "installs the latest version of gxapi_rails", :ruby => "1.9" do + install_gemfile <<-G + source "https://rubygems.org" + + gem "sass-rails" + gem "rails", "~> 3" + gem "gxapi_rails" + G + expect(out).to include("gxapi_rails 0.0.6") + end + + it "installs the latest version of i18n" do + install_gemfile <<-G + source "https://rubygems.org" + + gem "i18n", "~> 0.4" + gem "activesupport", "~> 3.0" + gem "activerecord", "~> 3.0" + gem "builder", "~> 2.1.2" + G + expect(out).to include("i18n 0.6.11") + expect(out).to include("activesupport 3.0.5") + end + # https://github.com/bundler/bundler/issues/1500 it "does not fail install because of gem plugins" do realworld_system_gems("open_gem --version 1.4.2", "rake --version 0.9.2") |