rubycut-babushka 0.10.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (171) hide show
  1. data/Gemfile +8 -0
  2. data/Gemfile.lock +31 -0
  3. data/README.markdown +246 -0
  4. data/Rakefile +26 -0
  5. data/bin/babushka +11 -0
  6. data/deps/babushka.rb +101 -0
  7. data/deps/dev.rb +12 -0
  8. data/deps/fhs.rb +31 -0
  9. data/deps/git.rb +29 -0
  10. data/deps/homebrew.rb +30 -0
  11. data/deps/os_x.rb +33 -0
  12. data/deps/packages.rb +22 -0
  13. data/deps/pkg_managers.rb +110 -0
  14. data/deps/ruby.rb +23 -0
  15. data/deps/rubygems.rb +24 -0
  16. data/deps/system.rb +10 -0
  17. data/deps/templates/app.rb +68 -0
  18. data/deps/templates/external.rb +12 -0
  19. data/deps/templates/installer.rb +31 -0
  20. data/deps/templates/managed.rb +105 -0
  21. data/deps/templates/ppa.rb +24 -0
  22. data/deps/templates/src.rb +42 -0
  23. data/deps/templates/tmbundle.rb +15 -0
  24. data/lib/babushka.rb +28 -0
  25. data/lib/babushka/accepts_block_for.rb +72 -0
  26. data/lib/babushka/accepts_list_for.rb +49 -0
  27. data/lib/babushka/accepts_value_for.rb +24 -0
  28. data/lib/babushka/base.rb +78 -0
  29. data/lib/babushka/bug_reporter.rb +55 -0
  30. data/lib/babushka/cmdline.rb +133 -0
  31. data/lib/babushka/cmdline/handler.rb +41 -0
  32. data/lib/babushka/cmdline/helpers.rb +127 -0
  33. data/lib/babushka/cmdline/parser.rb +69 -0
  34. data/lib/babushka/colorizer.rb +59 -0
  35. data/lib/babushka/core_patches/array.rb +171 -0
  36. data/lib/babushka/core_patches/blank.rb +22 -0
  37. data/lib/babushka/core_patches/bytes.rb +52 -0
  38. data/lib/babushka/core_patches/hash.rb +107 -0
  39. data/lib/babushka/core_patches/hashish.rb +14 -0
  40. data/lib/babushka/core_patches/integer.rb +25 -0
  41. data/lib/babushka/core_patches/io.rb +8 -0
  42. data/lib/babushka/core_patches/numeric.rb +16 -0
  43. data/lib/babushka/core_patches/object.rb +27 -0
  44. data/lib/babushka/core_patches/string.rb +116 -0
  45. data/lib/babushka/core_patches/symbol.rb +12 -0
  46. data/lib/babushka/core_patches/try.rb +15 -0
  47. data/lib/babushka/core_patches/uri.rb +24 -0
  48. data/lib/babushka/dep.rb +470 -0
  49. data/lib/babushka/dep_context.rb +18 -0
  50. data/lib/babushka/dep_definer.rb +115 -0
  51. data/lib/babushka/dep_pool.rb +49 -0
  52. data/lib/babushka/dep_runner.rb +85 -0
  53. data/lib/babushka/dsl.rb +26 -0
  54. data/lib/babushka/git_repo.rb +185 -0
  55. data/lib/babushka/helpers/git_helpers.rb +32 -0
  56. data/lib/babushka/helpers/log_helpers.rb +176 -0
  57. data/lib/babushka/helpers/path_helpers.rb +34 -0
  58. data/lib/babushka/helpers/run_helpers.rb +145 -0
  59. data/lib/babushka/helpers/shell_helpers.rb +229 -0
  60. data/lib/babushka/helpers/suggest_helpers.rb +16 -0
  61. data/lib/babushka/helpers/uri_helpers.rb +36 -0
  62. data/lib/babushka/ip.rb +160 -0
  63. data/lib/babushka/lambda_chooser.rb +40 -0
  64. data/lib/babushka/levenshtein.rb +125 -0
  65. data/lib/babushka/meta_dep.rb +65 -0
  66. data/lib/babushka/meta_dep_context.rb +15 -0
  67. data/lib/babushka/parameter.rb +143 -0
  68. data/lib/babushka/pkg_helper.rb +81 -0
  69. data/lib/babushka/pkg_helpers/apt_helper.rb +61 -0
  70. data/lib/babushka/pkg_helpers/base_helper.rb +19 -0
  71. data/lib/babushka/pkg_helpers/binpkgsrc_helper.rb +48 -0
  72. data/lib/babushka/pkg_helpers/binports_helper.rb +34 -0
  73. data/lib/babushka/pkg_helpers/brew_helper.rb +110 -0
  74. data/lib/babushka/pkg_helpers/gem_helper.rb +120 -0
  75. data/lib/babushka/pkg_helpers/macports_helper.rb +22 -0
  76. data/lib/babushka/pkg_helpers/npm_helper.rb +45 -0
  77. data/lib/babushka/pkg_helpers/pacman_helper.rb +27 -0
  78. data/lib/babushka/pkg_helpers/pip_helper.rb +45 -0
  79. data/lib/babushka/pkg_helpers/src_helper.rb +16 -0
  80. data/lib/babushka/pkg_helpers/yum_helper.rb +25 -0
  81. data/lib/babushka/popen.rb +40 -0
  82. data/lib/babushka/prompt.rb +176 -0
  83. data/lib/babushka/renderable.rb +67 -0
  84. data/lib/babushka/resource.rb +215 -0
  85. data/lib/babushka/run_reporter.rb +60 -0
  86. data/lib/babushka/shell.rb +108 -0
  87. data/lib/babushka/source.rb +216 -0
  88. data/lib/babushka/source_pool.rb +146 -0
  89. data/lib/babushka/system_definitions.rb +97 -0
  90. data/lib/babushka/system_profile.rb +210 -0
  91. data/lib/babushka/task.rb +142 -0
  92. data/lib/babushka/vars.rb +108 -0
  93. data/lib/babushka/version_of.rb +65 -0
  94. data/lib/babushka/version_str.rb +57 -0
  95. data/lib/babushka/xml_string.rb +28 -0
  96. data/lib/components.rb +82 -0
  97. data/lib/fancypath/fancypath.rb +200 -0
  98. data/lib/inkan/inkan.rb +76 -0
  99. data/spec/acceptance/acceptance.rb +43 -0
  100. data/spec/acceptance_helper.rb +113 -0
  101. data/spec/archives/Blah.app.zip +0 -0
  102. data/spec/archives/archive.tar +0 -0
  103. data/spec/archives/archive.tar.bz2 +0 -0
  104. data/spec/archives/archive.tar.gz +0 -0
  105. data/spec/archives/archive.tbz2 +0 -0
  106. data/spec/archives/archive.tgz +0 -0
  107. data/spec/archives/archive.zip +0 -0
  108. data/spec/archives/content.txt +5 -0
  109. data/spec/archives/invalid_archive +5 -0
  110. data/spec/archives/nested archive/content.txt +5 -0
  111. data/spec/archives/nested_archive.tar +0 -0
  112. data/spec/archives/really_a_gzip.zip +0 -0
  113. data/spec/archives/test-0.3.1.tgz +0 -0
  114. data/spec/archives/tgz_archive +0 -0
  115. data/spec/archives/zip_without_extension +0 -0
  116. data/spec/babushka/accepts_for_spec.rb +174 -0
  117. data/spec/babushka/accepts_for_support.rb +72 -0
  118. data/spec/babushka/cmdline/console_spec.rb +11 -0
  119. data/spec/babushka/cmdline/help_spec.rb +61 -0
  120. data/spec/babushka/cmdline/version_spec.rb +10 -0
  121. data/spec/babushka/core_patches_spec.rb +171 -0
  122. data/spec/babushka/dep_context_spec.rb +58 -0
  123. data/spec/babushka/dep_definer_spec.rb +152 -0
  124. data/spec/babushka/dep_definer_support.rb +36 -0
  125. data/spec/babushka/dep_spec.rb +567 -0
  126. data/spec/babushka/dep_support.rb +29 -0
  127. data/spec/babushka/deps_spec.rb +113 -0
  128. data/spec/babushka/gem_helper_spec.rb +90 -0
  129. data/spec/babushka/git_repo_spec.rb +396 -0
  130. data/spec/babushka/ip_spec.rb +131 -0
  131. data/spec/babushka/lambda_chooser_spec.rb +115 -0
  132. data/spec/babushka/meta_dep_definer_spec.rb +127 -0
  133. data/spec/babushka/meta_dep_wrapper_spec.rb +32 -0
  134. data/spec/babushka/parameter_spec.rb +135 -0
  135. data/spec/babushka/path_helpers_spec.rb +102 -0
  136. data/spec/babushka/prompt_spec.rb +188 -0
  137. data/spec/babushka/renderable_spec.rb +100 -0
  138. data/spec/babushka/resource_spec.rb +141 -0
  139. data/spec/babushka/run_helpers_spec.rb +26 -0
  140. data/spec/babushka/shell_helpers_spec.rb +244 -0
  141. data/spec/babushka/shell_spec.rb +19 -0
  142. data/spec/babushka/source_pool_spec.rb +320 -0
  143. data/spec/babushka/source_pool_support.rb +31 -0
  144. data/spec/babushka/source_spec.rb +382 -0
  145. data/spec/babushka/source_support.rb +17 -0
  146. data/spec/babushka/system_profile_spec.rb +61 -0
  147. data/spec/babushka/task_spec.rb +141 -0
  148. data/spec/babushka/uri_spec.rb +13 -0
  149. data/spec/babushka/vars_spec.rb +59 -0
  150. data/spec/babushka/version_of_spec.rb +110 -0
  151. data/spec/babushka/version_str_spec.rb +130 -0
  152. data/spec/babushka/version_str_support.rb +37 -0
  153. data/spec/babushka/xml_string_spec.rb +98 -0
  154. data/spec/deps/bad/broken.rb +7 -0
  155. data/spec/deps/bad/working.rb +3 -0
  156. data/spec/deps/good/meta.rb +14 -0
  157. data/spec/deps/good/test.rb +11 -0
  158. data/spec/deps/outer/deps.rb +19 -0
  159. data/spec/deps/outer/more deps.rb +11 -0
  160. data/spec/deps/params/params.rb +10 -0
  161. data/spec/fancypath/fancypath_spec.rb +272 -0
  162. data/spec/fancypath_support.rb +10 -0
  163. data/spec/inkan/inkan_spec.rb +217 -0
  164. data/spec/renderable/different_example.conf.erb +4 -0
  165. data/spec/renderable/example.conf.erb +3 -0
  166. data/spec/renderable/example.sh +6 -0
  167. data/spec/renderable/with_binding.conf.erb +4 -0
  168. data/spec/renderable/xml_example.conf.erb +8 -0
  169. data/spec/repos/remote.git.tgz +0 -0
  170. data/spec/spec_helper.rb +87 -0
  171. metadata +238 -0
@@ -0,0 +1,18 @@
1
+ module Babushka
2
+ class DepContext < DepDefiner
3
+ include DepRunner
4
+
5
+ accepts_list_for :desc
6
+ accepts_list_for :requires
7
+ accepts_list_for :requires_when_unmet
8
+ accepts_value_for :run_in
9
+
10
+ accepts_block_for :setup
11
+ accepts_block_for :met?
12
+
13
+ accepts_block_for :prepare
14
+ accepts_block_for :before
15
+ accepts_block_for :meet
16
+ accepts_block_for :after
17
+ end
18
+ end
@@ -0,0 +1,115 @@
1
+ module Babushka
2
+ class DepDefiner
3
+ include LogHelpers
4
+ extend LogHelpers
5
+ include ShellHelpers
6
+ extend ShellHelpers
7
+ include PathHelpers
8
+ extend PathHelpers
9
+ include RunHelpers
10
+ extend RunHelpers
11
+
12
+ include Prompt::Helpers
13
+ extend Prompt::Helpers
14
+ include VersionOf::Helpers
15
+ extend VersionOf::Helpers
16
+
17
+ include AcceptsListFor
18
+ include AcceptsValueFor
19
+ include AcceptsBlockFor
20
+
21
+ attr_reader :dependency, :payload, :block
22
+
23
+ def name; dependency.name end
24
+ def basename; dependency.basename end
25
+ def load_path; dependency.load_path end
26
+
27
+ include Vars::Helpers
28
+ extend Vars::Helpers
29
+
30
+ def initialize dep, &block
31
+ @dependency = dep
32
+ @payload = {}
33
+ @block = block
34
+ end
35
+
36
+ def define!
37
+ define_params!
38
+
39
+ unless block.nil?
40
+ raise "Dep block arguments aren't supported anymore. Instead, specify parameter names as symbols after the dep name. More details here: http://github.com/benhoskings/babushka/commit/40054c2" if block.arity > 0
41
+ instance_eval(&block)
42
+ end
43
+ end
44
+
45
+ def result message, opts = {}
46
+ opts[:result].tap {
47
+ dependency.result_message = message
48
+ }
49
+ end
50
+
51
+ def met message
52
+ result message, :result => true
53
+ end
54
+
55
+ def unmet message
56
+ result message, :result => false
57
+ end
58
+
59
+ def unmeetable message
60
+ raise Babushka::UnmeetableDep, message
61
+ end
62
+
63
+ def file_and_line
64
+ get_file_and_line_for(block)
65
+ end
66
+
67
+ def file_and_line_for block_name
68
+ get_file_and_line_for send(block_name) if has_block? block_name
69
+ end
70
+
71
+ def get_file_and_line_for blk
72
+ blk.inspect.scan(/\#\<Proc\:0x[0-9a-f]+\@([^:]+):(\d+)>/).flatten
73
+ end
74
+
75
+ private
76
+
77
+ def define_params!
78
+ dependency.params.each {|param|
79
+ if respond_to?(param)
80
+ raise DepParameterError, "You can't use #{param.inspect} as a parameter (on '#{dependency.name}'), because that's already a method on #{method(param).owner}."
81
+ else
82
+ metaclass.send :define_method, param do
83
+ dependency.args[param] ||= Parameter.new(param)
84
+ end
85
+ end
86
+ }
87
+ end
88
+
89
+ def pkg_manager
90
+ BaseHelper
91
+ end
92
+
93
+ def on platform, &blk
94
+ if [*chooser].include? platform
95
+ @current_platform = platform
96
+ blk.call.tap {
97
+ @current_platform = nil
98
+ }
99
+ end
100
+ end
101
+
102
+ def chooser
103
+ Base.host.match_list
104
+ end
105
+
106
+ def chooser_choices
107
+ SystemDefinitions.all_tokens
108
+ end
109
+
110
+ def self.source_template
111
+ Dep.base_template
112
+ end
113
+
114
+ end
115
+ end
@@ -0,0 +1,49 @@
1
+ module Babushka
2
+ class DepPool
3
+
4
+ def initialize source
5
+ clear!
6
+ @source = source
7
+ end
8
+
9
+ def count
10
+ @pool.length
11
+ end
12
+
13
+ def names
14
+ @pool.keys
15
+ end
16
+ def items
17
+ @pool.values
18
+ end
19
+ def for spec
20
+ spec.respond_to?(:name) ? @pool[spec.name] : @pool[spec]
21
+ end
22
+
23
+ def add_dep name, params, block
24
+ if self.for name
25
+ self.for name
26
+ else
27
+ opts = params.extract_options!
28
+ Dep.new name, @source, params, opts, block
29
+ end
30
+ end
31
+
32
+ def add_template name, in_opts, block
33
+ MetaDep.for name, @source, in_opts, &block
34
+ end
35
+
36
+ def clear!
37
+ @pool = {}
38
+ end
39
+ def uncache!
40
+ items.each {|i| i.send :uncache! }
41
+ end
42
+
43
+ def register item
44
+ raise "Already registered '#{item.name}'." if @pool.has_key?(item.name)
45
+ @pool[item.name] = item
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,85 @@
1
+ module Babushka
2
+ module DepRunner
3
+ include GitHelpers
4
+ include UriHelpers
5
+
6
+ private
7
+
8
+ # TODO: solve cmd/app and string/version handling better.
9
+ def in_path? provided_list
10
+ apps, command_names = [*provided_list].partition {|i| i.to_s[/\.app\/?$/] }
11
+ commands = command_names.versions
12
+ apps_in_path?(apps) and cmds_in_path?(commands) and matching_versions?(commands)
13
+ end
14
+
15
+ def apps_in_path? apps
16
+ present, missing = [*apps].partition {|app_name| app_dir(app_name) }
17
+
18
+ missing.empty?.tap {|result|
19
+ if result
20
+ log "#{present.map {|i| "'#{i}'" }.to_list} #{present.length == 1 ? 'is' : 'are'} present." unless present.empty?
21
+ else
22
+ log "#{missing.map {|i| "'#{i}'" }.to_list} #{missing.length == 1 ? 'is' : 'are'}n't present anywhere in $PATH."
23
+ end
24
+ }
25
+ end
26
+
27
+ def cmds_in_path? commands
28
+ dir_hash = [*commands].group_by {|cmd| cmd_dir(cmd.name) }
29
+
30
+ if dir_hash.keys.compact.length > 1
31
+ unmeetable "The commands for '#{name}' run from more than one place.\n" +
32
+ dir_hash.values.map {|cmds|
33
+ cmd_location_str_for cmds
34
+ }.to_list(:oxford => true, :conj => 'but').end_with('.')
35
+ else
36
+ cmds = dir_hash.values.first
37
+ dir_hash[nil].blank?.tap {|result|
38
+ if result
39
+ log cmd_location_str_for(cmds).end_with('.') unless cmds.blank?
40
+ else
41
+ log "#{dir_hash[nil].map {|i| "'#{i}'" }.to_list} #{dir_hash[nil].length == 1 ? 'is' : 'are'} missing."
42
+ end
43
+ }
44
+ end
45
+ end
46
+
47
+ def matching_versions? commands
48
+ versions = commands.select {|cmd|
49
+ !cmd.version.nil?
50
+ }.inject({}) {|hsh,cmd|
51
+ hsh[cmd] = (shell("#{cmd.name} --version") || '').split(/[\s\-]/).detect {|piece|
52
+ begin
53
+ cmd.matches? piece.to_version
54
+ rescue VersionStrError
55
+ false
56
+ end
57
+ }
58
+ log "#{cmd.name} is #{hsh[cmd]}, which is#{"n't" unless hsh[cmd]} #{cmd.version}.", :as => (:ok if hsh[cmd])
59
+ hsh
60
+ }
61
+ versions.values.all?
62
+ end
63
+
64
+ def app_dir app_name
65
+ prefix.find {|app_path|
66
+ (app_path.to_s / app_name).glob.select {|entry|
67
+ (entry / 'Contents/MacOS').exists?
68
+ }.first
69
+ }
70
+ end
71
+
72
+ def cmd_location_str_for cmds
73
+ "#{cmds.map {|i| "'#{i.name}'" }.to_list(:conj => '&')} run#{'s' if cmds.length == 1} from #{cmd_dir(cmds.first.name)}"
74
+ end
75
+
76
+ def call_task task_name
77
+ if (task_block = send(task_name)).nil?
78
+ true
79
+ else
80
+ instance_eval(&task_block)
81
+ end
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,26 @@
1
+ module Babushka
2
+ module DSL
3
+ # Use +spec+ to look up a dep. Because +spec+ might include a source
4
+ # prefix, the dep this method returns could be from any of the currently
5
+ # known sources.
6
+ # If no dep matching +spec+ is found, nil is returned.
7
+ def Dep spec, opts = {}
8
+ Base.sources.dep_for spec, opts
9
+ end
10
+
11
+ # Define and return a dep named +name+, and whose implementation is found
12
+ # in +block+. This is the usual top-level entry point of the babushka
13
+ # DSL (along with +meta+); templated or not, this is how deps are
14
+ # defined.
15
+ def dep name, *params, &block
16
+ Base.sources.current_load_source.deps.add_dep name, params, block
17
+ end
18
+
19
+ # Define and return a meta dep named +name+, and whose implementation is
20
+ # found in +block+. This method, along with +dep, together are the
21
+ # top level of babushka's DSL.
22
+ def meta name, opts = {}, &block
23
+ Base.sources.current_load_source.templates.add_template name, opts, block
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,185 @@
1
+ module Babushka
2
+ class GitRepoError < StandardError
3
+ end
4
+ class GitRepoExists < GitRepoError
5
+ end
6
+ # This class is used for manipulating git repositories (see {#initialize} how to start). Mostly it provides shortcuts to most often used git commands.
7
+ # @example Example of usage:
8
+ # @repo ||= Babushka::GitRepo.new('.')
9
+ # @repo.clone "https://github.com/benhoskings/babushka"
10
+ # @repo.checkout! "devel"
11
+ #
12
+ # For practical example how to use GitRepo class, see Ben Hoskings {https://github.com/benhoskings/babushka-deps/blob/master/push.rb push dep}
13
+ #
14
+ # Methods for checking repositrory state:
15
+ #
16
+ # * {#clean?}
17
+ # * {#dirty?}
18
+ # * {#include?} ref
19
+ # * {#ahead?}
20
+ # * {#behind?}
21
+ # * {#rebasing?}
22
+ # * {#applying?}
23
+ # * {#merging?}
24
+ # * {#bisecting?}
25
+ # * {#rebase_merging?}
26
+ # * {#rebasing_interactively?}
27
+ #
28
+ # Methods for repository operations:
29
+ #
30
+ # * {#clone!}
31
+ # * {#branch!}
32
+ # * {#track!}
33
+ # * {#checkout!}
34
+ # * {#reset_hard!} refspec
35
+
36
+ class GitRepo
37
+ include ShellHelpers
38
+ extend ShellHelpers
39
+
40
+ def self.repo_for path
41
+ maybe = shell("git rev-parse --git-dir", :cd => path) if path.p.dir?
42
+ maybe == '.git' ? path.p : maybe / '..' unless maybe.nil?
43
+ end
44
+ # @example Initialize repo in current dir
45
+ # @repo ||= Babushka::GitRepo.new('.')
46
+ def initialize path
47
+ @raw_path = path
48
+ end
49
+
50
+ def path
51
+ @path ||= @raw_path.p
52
+ end
53
+
54
+ def root
55
+ @root ||= self.class.repo_for(path)
56
+ end
57
+
58
+ def git_dir
59
+ root / '.git'
60
+ end
61
+
62
+ def exists?
63
+ !root.nil? && root.exists?
64
+ end
65
+ # executes shell command setting current directory to repository root
66
+ #
67
+ def repo_shell cmd, opts = {}, &block
68
+ if !exists?
69
+ raise GitRepoError, "There is no repo at #{@path}."
70
+ else
71
+ shell cmd, opts.merge(:cd => root), &block
72
+ end
73
+ end
74
+
75
+ def clean?
76
+ repo_shell("git status") # Sometimes git caches invalid index info; this clears it.
77
+ repo_shell("git diff-index --name-status HEAD", &:stdout).blank?
78
+ end
79
+
80
+ def dirty?
81
+ !clean?
82
+ end
83
+
84
+ def include? ref
85
+ repo_shell("git rev-list -n 1 '#{ref}'")
86
+ end
87
+
88
+ def ahead?
89
+ !remote_branch_exists? ||
90
+ !repo_shell("git rev-list origin/#{current_branch}..").split("\n").empty?
91
+ end
92
+
93
+ def behind?
94
+ remote_branch_exists? &&
95
+ !repo_shell("git rev-list ..origin/#{current_branch}").split("\n").empty?
96
+ end
97
+
98
+ def rebasing?
99
+ %w[rebase rebase-apply ../.dotest].any? {|d|
100
+ (git_dir / d).exists?
101
+ } or rebase_merging? or rebasing_interactively?
102
+ end
103
+
104
+ def applying?
105
+ %w[rebase rebase-apply ../.dotest].any? {|d|
106
+ (git_dir / d / 'applying').exists?
107
+ }
108
+ end
109
+
110
+ def merging?
111
+ (git_dir / 'MERGE_HEAD').exists? or rebase_merging?
112
+ end
113
+
114
+ def bisecting?
115
+ (git_dir / 'BISECT_LOG').exists?
116
+ end
117
+
118
+ def rebase_merging?
119
+ %w[rebase-merge .dotest-merge].any? {|d|
120
+ (git_dir / d).exists?
121
+ }
122
+ end
123
+
124
+ def rebasing_interactively?
125
+ %w[rebase-merge .dotest-merge].any? {|d|
126
+ (git_dir / d / 'interactive').exists?
127
+ }
128
+ end
129
+
130
+ def branches
131
+ repo_shell('git branch').split("\n").map {|l| l.sub(/^[* ]+/, '') }
132
+ end
133
+
134
+ def current_branch
135
+ repo_shell("cat .git/HEAD").strip.sub(/^.*refs\/heads\//, '')
136
+ end
137
+
138
+ def current_head
139
+ repo_shell("git rev-parse --short HEAD")
140
+ end
141
+
142
+ def current_full_head
143
+ repo_shell("git rev-parse HEAD")
144
+ end
145
+
146
+ def remote_branch_exists?
147
+ repo_shell('git branch -a').split("\n").map(&:strip).detect {|b|
148
+ b[/^(remotes\/)?origin\/#{current_branch}$/]
149
+ }
150
+ end
151
+
152
+ def clone! from
153
+ raise GitRepoExists, "Can't clone #{from} to existing path #{path}." if exists?
154
+ shell("git clone '#{from}' '#{path.basename}'", :cd => path.parent, :create => true) {|shell|
155
+ shell.ok? || raise(GitRepoError, "Couldn't clone to #{path}: #{error_message_for shell.stderr}")
156
+ }
157
+ end
158
+
159
+ def branch! branch
160
+ repo_shell("git branch '#{branch}'")
161
+ end
162
+
163
+ def track! branch
164
+ repo_shell("git checkout -t '#{branch}'")
165
+ end
166
+
167
+ def checkout! branch
168
+ repo_shell("git checkout '#{branch}'")
169
+ end
170
+
171
+ def reset_hard! refspec = 'HEAD'
172
+ repo_shell("git reset --hard #{refspec}")
173
+ end
174
+
175
+ def inspect
176
+ "#<GitRepo:#{root} : #{current_branch}@#{current_head}#{' (dirty)' if dirty?}>"
177
+ end
178
+
179
+ private
180
+
181
+ def error_message_for git_error
182
+ git_error.sub(/^fatal\: /, '').sub(/\n.*$/m, '').end_with('.')
183
+ end
184
+ end
185
+ end