1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
module Bundler
module Source
class Git < Path
# The GitProxy is responsible to iteract with git repositories.
# All actions required by the Git source is encapsualted in this
# object.
class GitProxy
attr_accessor :path, :uri, :ref
attr_writer :revision
def initialize(path, uri, ref, revision=nil, &allow)
@path = path
@uri = uri
@ref = ref
@revision = revision
@allow = allow || Proc.new { true }
end
def revision
@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
def checkout
if path.exist?
return if has_revision_cached?
Bundler.ui.info "Updating #{uri}"
in_path do
git %|fetch --force --quiet --tags #{uri_escaped} "refs/heads/*:refs/heads/*"|
end
else
Bundler.ui.info "Fetching #{uri}"
FileUtils.mkdir_p(path.dirname)
clone_command = %|clone #{uri_escaped} "#{path}" --bare --no-hardlinks|
clone_command = "#{clone_command} --quiet" if Bundler.ui.quiet?
git clone_command
end
end
def copy_to(destination, submodules=false)
unless File.exist?(destination.join(".git"))
FileUtils.mkdir_p(destination.dirname)
FileUtils.rm_rf(destination)
git %|clone --no-checkout "#{path}" "#{destination}"|
File.chmod((0777 & ~File.umask), destination)
end
SharedHelpers.chdir(destination) do
git %|fetch --force --quiet --tags "#{path}"|
git "reset --hard #{@revision}"
if submodules
git "submodule update --init --recursive"
end
end
end
private
# 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 = SharedHelpers.with_clean_git_env { %x{git #{command}} }
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
end
out
else
raise GitError, "Bundler is trying to run a `git #{command}` at runtime. You probably need to run `bundle install`. However, " \
"this error message could probably be more useful. Please submit a ticket at http://github.com/bundler/bundler/issues " \
"with steps to reproduce as well as the following\n\nCALLER: #{caller.join("\n")}"
end
end
def has_revision_cached?
return unless @revision
in_path { git("cat-file -e #{@revision}") }
true
rescue GitError
false
end
# Escape the URI for git commands
def uri_escaped
if Bundler::WINDOWS
# Windows quoting requires double quotes only, with double quotes
# inside the string escaped by being doubled.
'"' + uri.gsub('"') {|s| '""'} + '"'
else
# Bash requires single quoted strings, with the single quotes escaped
# by ending the string, escaping the quote, and restarting the string.
"'" + uri.gsub("'") {|s| "'\\''"} + "'"
end
end
def allow?
@allow.call
end
def in_path(&blk)
checkout unless path.exist?
SharedHelpers.chdir(path, &blk)
end
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
end
end
end
|