diff options
author | José Valim <jose.valim@plataformatec.com.br> | 2012-03-19 09:39:15 +0100 |
---|---|---|
committer | lolwut <lol@wut.com> | 2012-03-19 19:02:22 +0100 |
commit | 7ea338f06332cb397e3d4ead548463e39c863932 (patch) | |
tree | 69fe44c1ed2dbde62eba866272b45d8dcc19fc35 /lib/bundler/source.rb | |
parent | 92d6ede5011f3b67a0ea1133217fca46f9a3a4cb (diff) | |
download | bundler-7ea338f06332cb397e3d4ead548463e39c863932.tar.gz |
Initial implementation of local git repos.
The proposal of this patch is to implement a functionality
that allow developers to work against a git repository locally.
This can be achieved by setting up a local override:
bundle config rack.local ~/path/to/local/rack
Now, instead of checking out a git repository, the local override
will be used. This implies a few things:
Similar to path, every time the local git repository change,
changes will be automatically picked up by Bundler; This means a
commit in the local git repo will update the revision in the
Gemfile.lock to the local git repo revision. This requires the
same attention as git submodules. Before pushing to remote,
you need to ensure the local override was pushed, otherwise you
may point to a commit that only exists in your local machine.
Also, we are doing many checks to ensure a developer won't work
with invalid references. Particularly, we force a developer to
specify a branch in the Gemfile in order to use local overrides
and, if your local repository is on another branch, we will abort.
This allows a developer to change between topic or release branches
without accidentally changing the reference in the Gemfile.lock.
We also ensure that the current revision in the Gemfile.lock exists
in the current local override. By doing this, we force the local
override to fetch the latest changes in the remotes.
There are a few things missing before this change can be merged in:
1) We could improve `bundle check` to validate the conditions above.
With this in mind, a developer could add a pre-commit hook that
invokes `bundle check` to ensure he isn't pushing an old or invalid
reference. However, it will be up to the developer to ensure his
local overrides are not dirty or that they were pushed to remote.
2) Currently, every time there is a local override, we are automatically
by passing locked specs, regardless if there was a change or not.
We need to improve this scenario in order to improve performance.
3) `bundle config foo bar` sets the configuration value for the
current user (~). We need to be able to set project configuration
and delete them as well.
Diffstat (limited to 'lib/bundler/source.rb')
-rw-r--r-- | lib/bundler/source.rb | 107 |
1 files changed, 95 insertions, 12 deletions
diff --git a/lib/bundler/source.rb b/lib/bundler/source.rb index d53bf388..23676dec 100644 --- a/lib/bundler/source.rb +++ b/lib/bundler/source.rb @@ -494,10 +494,19 @@ module Bundler end def revision - @revision ||= if allow? - in_path { git("rev-parse #{ref}").strip } - else - raise GitError, "The git source #{uri} is not yet checked out. Please run `bundle install` before trying to start your application" + @revision ||= allowed_in_path { git("rev-parse #{ref}").strip } + end + + def branch + @branch ||= allowed_in_path do + git("branch") =~ /^\* (.*)$/ && $1.strip + end + end + + def contains?(commit) + allowed_in_path do + result = git_null("branch --contains #{commit}") + $? == 0 && result =~ /^\* (.*)$/ end end @@ -536,11 +545,24 @@ module Bundler private - def git(command) + # TODO: Do not rely on /dev/null. + # Given that open3 is not cross platform until Ruby 1.9.3, + # the best solution is to pipe to /dev/null if it exists. + # If it doesn't, everything will work fine, but the user + # will get the $stderr messages as well. + def git_null(command) + if !Bundler::WINDOWS && File.exist?("/dev/null") + git("#{command} 2>/dev/null", false) + else + git(command, false) + end + end + + def git(command, check_errors=true) if allow? out = %x{git #{command}} - if $?.exitstatus != 0 + if check_errors && $?.exitstatus != 0 msg = "Git error: command `git #{command}` in directory #{Dir.pwd} has failed." msg << "\nIf this error persists you could try removing the cache directory '#{path}'" if path.exist? raise GitError, msg @@ -574,13 +596,21 @@ module Bundler end end + def allow? + @allow.call + end + def in_path(&blk) checkout unless path.exist? Dir.chdir(path, &blk) end - def allow? - @allow.call + def allowed_in_path + if allow? + in_path { yield } + else + raise GitError, "The git source #{uri} is not yet checked out. Please run `bundle install` before trying to start your application" + end end end @@ -597,6 +627,7 @@ module Bundler @submodules = options["submodules"] @update = false @installed = nil + @local = false end def self.from_lock(options) @@ -626,8 +657,14 @@ module Bundler alias == eql? def to_s - sref = options["ref"] ? shortref_for_display(options["ref"]) : ref - "#{uri} (at #{sref})" + at = if local? + path + elsif options["ref"] + shortref_for_display(options["ref"]) + else + ref + end + "#{uri} (at #{at})" end def name @@ -652,6 +689,40 @@ module Bundler git_proxy.revision = nil end + def local_override!(path) + path = Pathname.new(path) + path = path.expand_path(Bundler.root) unless path.relative? + + unless options["branch"] + raise GitError, "Cannot use local override for #{name} at #{path} because " \ + ":branch is not specified in Gemfile. Specify a branch or check " \ + "`bundle config --delete` to remove the local override" + end + + unless path.exist? + raise GitError, "Cannot use local override for #{name} because #{path} " \ + "does not exist. Check `bundle config --delete` to remove the local override" + end + + @local = true + @local_specs = nil + @git_proxy = GitProxy.new(path, uri, ref) + @cache_path = @install_path = path + + if git_proxy.branch != options["branch"] + raise GitError, "Local override for #{name} at #{path} is using branch " \ + "#{git_proxy.branch} but Gemfile specifies #{options["branch"]}" + end + + rev = cached_revision + + if rev && rev != git_proxy.revision && !git_proxy.contains?(rev) + raise GitError, "The Gemfile lock is pointing to revision #{shortref_for_display(rev)} " \ + "but the current branch in your local override for #{name} does not contain such commit. " \ + "Please make sure your branch is up to date." + end + end + # TODO: actually cache git specs def specs(*) if requires_checkout? && !@update @@ -692,8 +763,12 @@ module Bundler private + def local? + @local + end + def requires_checkout? - allow_git_ops? + allow_git_ops? && !local? end def base_name @@ -724,12 +799,20 @@ module Bundler @allow_remote || @allow_cached end + def cached_revision + options["revision"] + end + def revision git_proxy.revision end + def cached? + cache_path.exist? + end + def git_proxy - @git_proxy ||= GitProxy.new(cache_path, uri, ref, options["revision"]){ allow_git_ops? } + @git_proxy ||= GitProxy.new(cache_path, uri, ref, cached_revision){ allow_git_ops? } end end |