mortar 0.1.0
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.
- data/README.md +36 -0
- data/bin/mortar +13 -0
- data/lib/mortar.rb +23 -0
- data/lib/mortar/auth.rb +312 -0
- data/lib/mortar/cli.rb +54 -0
- data/lib/mortar/command.rb +267 -0
- data/lib/mortar/command/auth.rb +96 -0
- data/lib/mortar/command/base.rb +319 -0
- data/lib/mortar/command/clusters.rb +41 -0
- data/lib/mortar/command/describe.rb +97 -0
- data/lib/mortar/command/generate.rb +121 -0
- data/lib/mortar/command/help.rb +166 -0
- data/lib/mortar/command/illustrate.rb +97 -0
- data/lib/mortar/command/jobs.rb +174 -0
- data/lib/mortar/command/pigscripts.rb +45 -0
- data/lib/mortar/command/projects.rb +128 -0
- data/lib/mortar/command/validate.rb +94 -0
- data/lib/mortar/command/version.rb +42 -0
- data/lib/mortar/errors.rb +24 -0
- data/lib/mortar/generators/generator_base.rb +107 -0
- data/lib/mortar/generators/macro_generator.rb +37 -0
- data/lib/mortar/generators/pigscript_generator.rb +40 -0
- data/lib/mortar/generators/project_generator.rb +67 -0
- data/lib/mortar/generators/udf_generator.rb +28 -0
- data/lib/mortar/git.rb +233 -0
- data/lib/mortar/helpers.rb +488 -0
- data/lib/mortar/project.rb +156 -0
- data/lib/mortar/snapshot.rb +39 -0
- data/lib/mortar/templates/macro/macro.pig +14 -0
- data/lib/mortar/templates/pigscript/pigscript.pig +38 -0
- data/lib/mortar/templates/pigscript/python_udf.py +13 -0
- data/lib/mortar/templates/project/Gemfile +3 -0
- data/lib/mortar/templates/project/README.md +8 -0
- data/lib/mortar/templates/project/gitignore +4 -0
- data/lib/mortar/templates/project/macros/gitkeep +0 -0
- data/lib/mortar/templates/project/pigscripts/pigscript.pig +35 -0
- data/lib/mortar/templates/project/udfs/python/python_udf.py +13 -0
- data/lib/mortar/templates/udf/python_udf.py +13 -0
- data/lib/mortar/version.rb +20 -0
- data/lib/vendor/mortar/okjson.rb +598 -0
- data/lib/vendor/mortar/uuid.rb +312 -0
- data/spec/mortar/auth_spec.rb +156 -0
- data/spec/mortar/command/auth_spec.rb +46 -0
- data/spec/mortar/command/base_spec.rb +82 -0
- data/spec/mortar/command/clusters_spec.rb +61 -0
- data/spec/mortar/command/describe_spec.rb +135 -0
- data/spec/mortar/command/generate_spec.rb +139 -0
- data/spec/mortar/command/illustrate_spec.rb +140 -0
- data/spec/mortar/command/jobs_spec.rb +364 -0
- data/spec/mortar/command/pigscripts_spec.rb +70 -0
- data/spec/mortar/command/projects_spec.rb +165 -0
- data/spec/mortar/command/validate_spec.rb +119 -0
- data/spec/mortar/command_spec.rb +122 -0
- data/spec/mortar/git_spec.rb +278 -0
- data/spec/mortar/helpers_spec.rb +82 -0
- data/spec/mortar/project_spec.rb +76 -0
- data/spec/mortar/snapshot_spec.rb +46 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +278 -0
- data/spec/support/display_message_matcher.rb +68 -0
- metadata +259 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2012 Mortar Data Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
module Mortar
|
18
|
+
class CLI
|
19
|
+
module Errors
|
20
|
+
class InvalidGithubUsername < StandardError; end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2012 Mortar Data Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require "erb"
|
18
|
+
require "fileutils"
|
19
|
+
|
20
|
+
module Mortar
|
21
|
+
module Generators
|
22
|
+
class Base
|
23
|
+
include Mortar::Helpers
|
24
|
+
|
25
|
+
def initialize()
|
26
|
+
# This is a really ugly way to turn the subclass name 'Mortar::Generators::ProjectGenerator'
|
27
|
+
# into 'project' so we can use it to find the appropriate templates folder
|
28
|
+
|
29
|
+
generator_type = self.class.name.split("::")[2].match(/.*(?=([A-Z]))/)[0].downcase
|
30
|
+
@src_path = File.expand_path("../../templates/#{generator_type}", __FILE__)
|
31
|
+
@dest_path = Dir.pwd
|
32
|
+
@rel_path = ""
|
33
|
+
@binding_variables = {}
|
34
|
+
end
|
35
|
+
|
36
|
+
def inside(folder)
|
37
|
+
rel_backup = @rel_path
|
38
|
+
@rel_path = File.join(@rel_path, folder)
|
39
|
+
yield
|
40
|
+
@rel_path = rel_backup
|
41
|
+
end
|
42
|
+
|
43
|
+
def copy_file(src_file, dest_file, options={ :recursive => false })
|
44
|
+
src_path = File.join(@src_path, @rel_path, src_file)
|
45
|
+
dest_path = File.join(@dest_path, @rel_path, dest_file)
|
46
|
+
msg = File.join(@rel_path, dest_file)[1..-1]
|
47
|
+
|
48
|
+
if File.exists?(dest_path)
|
49
|
+
if FileUtils.compare_file(src_path, dest_path)
|
50
|
+
display_identical(msg)
|
51
|
+
else
|
52
|
+
display_conflict(msg)
|
53
|
+
end
|
54
|
+
else
|
55
|
+
display_create(msg)
|
56
|
+
FileUtils.mkdir_p(File.dirname(dest_path)) if options[:recursive]
|
57
|
+
FileUtils.cp(src_path, dest_path)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def mkdir(folder, options={ :verbose => true })
|
62
|
+
dest_path = File.join(@dest_path, @rel_path, folder)
|
63
|
+
msg = File.join(@rel_path, folder)[1..-1]
|
64
|
+
|
65
|
+
if File.exists?(dest_path)
|
66
|
+
display_exists(options[:verbose] ? msg : "")
|
67
|
+
else
|
68
|
+
display_create(options[:verbose] ? msg : "")
|
69
|
+
FileUtils.mkdir(dest_path)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def generate_file(src_file, dest_file, options={ :recursive => false })
|
74
|
+
src_path = File.join(@src_path, @rel_path, src_file)
|
75
|
+
dest_path = File.join(@dest_path, @rel_path, dest_file)
|
76
|
+
msg = File.join(@rel_path, dest_file)[1..-1]
|
77
|
+
|
78
|
+
erb = ERB.new(File.read(src_path), 0, "%<>")
|
79
|
+
|
80
|
+
result = erb.result(@script_binding)
|
81
|
+
|
82
|
+
|
83
|
+
if File.exists?(dest_path)
|
84
|
+
if result == File.read(dest_path)
|
85
|
+
display_identical(msg)
|
86
|
+
else
|
87
|
+
display_conflict(msg)
|
88
|
+
end
|
89
|
+
else
|
90
|
+
FileUtils.mkdir_p(File.dirname(dest_path)) if options[:recursive]
|
91
|
+
file = File.new(dest_path, "w")
|
92
|
+
file.write(result)
|
93
|
+
file.close
|
94
|
+
display_create(msg)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
protected
|
99
|
+
|
100
|
+
def set_script_binding(options)
|
101
|
+
options = options
|
102
|
+
binding
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2012 Mortar Data Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require "mortar/generators/generator_base"
|
18
|
+
module Mortar
|
19
|
+
module Generators
|
20
|
+
class MacroGenerator < Base
|
21
|
+
|
22
|
+
def generate_macro(macro_name, project, options)
|
23
|
+
set_script_binding(macro_name, options)
|
24
|
+
generate_file "macro.pig", "macros/#{macro_name}.pig", :recursive => true
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def set_script_binding(macro_name, options)
|
30
|
+
options = options
|
31
|
+
macro_name = macro_name
|
32
|
+
@script_binding = binding
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2012 Mortar Data Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require "mortar/generators/generator_base"
|
18
|
+
module Mortar
|
19
|
+
module Generators
|
20
|
+
class PigscriptGenerator < Base
|
21
|
+
|
22
|
+
def generate_pigscript(script_name, project, options)
|
23
|
+
set_script_binding(script_name, options)
|
24
|
+
|
25
|
+
|
26
|
+
generate_file "pigscript.pig", "pigscripts/#{script_name}.pig", :recursive => true
|
27
|
+
copy_file "python_udf.py", "udfs/python/#{script_name}.py", :recursive => true if not options[:skip_udf]
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def set_script_binding(script_name, options)
|
33
|
+
options = options
|
34
|
+
script_name = script_name
|
35
|
+
@script_binding = binding
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2012 Mortar Data Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require "mortar/generators/generator_base"
|
18
|
+
module Mortar
|
19
|
+
module Generators
|
20
|
+
class ProjectGenerator < Base
|
21
|
+
|
22
|
+
def generate_project(project_name, options)
|
23
|
+
|
24
|
+
set_script_binding(project_name, options)
|
25
|
+
mkdir project_name, :verbose => false
|
26
|
+
@dest_path = File.join(@dest_path, project_name)
|
27
|
+
|
28
|
+
copy_file "README.md", "README.md"
|
29
|
+
copy_file "gitignore", ".gitignore"
|
30
|
+
copy_file "Gemfile", "Gemfile"
|
31
|
+
|
32
|
+
mkdir "pigscripts"
|
33
|
+
|
34
|
+
inside "pigscripts" do
|
35
|
+
generate_file "pigscript.pig", "#{project_name}.pig"
|
36
|
+
end
|
37
|
+
|
38
|
+
mkdir "macros"
|
39
|
+
|
40
|
+
inside "macros" do
|
41
|
+
copy_file "gitkeep", ".gitkeep"
|
42
|
+
end
|
43
|
+
|
44
|
+
mkdir "udfs"
|
45
|
+
|
46
|
+
inside "udfs" do
|
47
|
+
mkdir "python"
|
48
|
+
inside "python" do
|
49
|
+
copy_file "python_udf.py", "#{project_name}.py"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
display_run("bundle install")
|
54
|
+
`cd #{project_name} && bundle install && cd ..`
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
protected
|
59
|
+
|
60
|
+
def set_script_binding(project_name, options)
|
61
|
+
options = options
|
62
|
+
project_name = project_name
|
63
|
+
@script_binding = binding
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2012 Mortar Data Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require "mortar/generators/generator_base"
|
18
|
+
module Mortar
|
19
|
+
module Generators
|
20
|
+
class UDFGenerator < Base
|
21
|
+
|
22
|
+
def generate_python_udf(udf_name, project, options)
|
23
|
+
copy_file "python_udf.py", "udfs/python/#{udf_name}.py", :recursive => true
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/mortar/git.rb
ADDED
@@ -0,0 +1,233 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2012 Mortar Data Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require "vendor/mortar/uuid"
|
18
|
+
require "set"
|
19
|
+
|
20
|
+
module Mortar
|
21
|
+
module Git
|
22
|
+
|
23
|
+
class GitError < RuntimeError; end
|
24
|
+
|
25
|
+
class Git
|
26
|
+
|
27
|
+
#
|
28
|
+
# core commands
|
29
|
+
#
|
30
|
+
|
31
|
+
def has_git?
|
32
|
+
%x{ git --version }
|
33
|
+
$?.success?
|
34
|
+
end
|
35
|
+
|
36
|
+
def has_dot_git?
|
37
|
+
File.directory?(".git")
|
38
|
+
end
|
39
|
+
|
40
|
+
def git(args, check_success=true, check_git_directory=true)
|
41
|
+
unless has_git?
|
42
|
+
raise GitError, "git must be installed"
|
43
|
+
end
|
44
|
+
|
45
|
+
if check_git_directory && !has_dot_git?
|
46
|
+
raise GitError, "No .git directory found"
|
47
|
+
end
|
48
|
+
|
49
|
+
flattened_args = [args].flatten.compact.join(" ")
|
50
|
+
output = %x{ git #{flattened_args} 2>&1 }.strip
|
51
|
+
success = $?.success?
|
52
|
+
if check_success && (! success)
|
53
|
+
raise GitError, "Error executing 'git #{flattened_args}':\n#{output}"
|
54
|
+
end
|
55
|
+
output
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# snapshot
|
60
|
+
#
|
61
|
+
|
62
|
+
def create_snapshot_branch
|
63
|
+
# TODO: handle Ctrl-C in the middle
|
64
|
+
# TODO: can we do the equivalent of stash without changing the working directory
|
65
|
+
unless has_commits?
|
66
|
+
raise GitError, "No commits found in repository. You must do an initial commit to initialize the repository."
|
67
|
+
end
|
68
|
+
|
69
|
+
starting_branch = current_branch
|
70
|
+
snapshot_branch = "mortar-snapshot-#{Mortar::UUID.create_random.to_s}"
|
71
|
+
did_stash_changes = stash_working_dir(snapshot_branch)
|
72
|
+
begin
|
73
|
+
# checkout a new branch
|
74
|
+
git("checkout -b #{snapshot_branch}")
|
75
|
+
|
76
|
+
if did_stash_changes
|
77
|
+
# apply the topmost stash that we just created
|
78
|
+
git("stash apply stash@{0}")
|
79
|
+
end
|
80
|
+
|
81
|
+
add_untracked_files()
|
82
|
+
|
83
|
+
# commit the changes if there are any
|
84
|
+
if ! is_clean_working_directory?
|
85
|
+
git("commit -a -m \"mortar development snapshot commit\"")
|
86
|
+
end
|
87
|
+
|
88
|
+
ensure
|
89
|
+
|
90
|
+
# return to the starting branch
|
91
|
+
git("checkout #{starting_branch}")
|
92
|
+
|
93
|
+
# rebuild the original state of the working set
|
94
|
+
if did_stash_changes
|
95
|
+
git("stash pop stash@{0}")
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
snapshot_branch
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
# add
|
104
|
+
#
|
105
|
+
|
106
|
+
def add(path)
|
107
|
+
git("add #{path}")
|
108
|
+
end
|
109
|
+
|
110
|
+
def add_untracked_files
|
111
|
+
untracked_files.each do |untracked_file|
|
112
|
+
add untracked_file
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
# branch
|
118
|
+
#
|
119
|
+
|
120
|
+
def branches
|
121
|
+
git("branch")
|
122
|
+
end
|
123
|
+
|
124
|
+
def current_branch
|
125
|
+
branches.split("\n").each do |branch_listing|
|
126
|
+
|
127
|
+
# current branch will be the one that starts with *, e.g.
|
128
|
+
# not_my_current_branch
|
129
|
+
# * my_current_branch
|
130
|
+
if branch_listing =~ /^\*\s(\S*)/
|
131
|
+
return $1
|
132
|
+
end
|
133
|
+
end
|
134
|
+
raise GitError, "Unable to find current branch in list #{branches}"
|
135
|
+
end
|
136
|
+
|
137
|
+
def branch_delete(branch_name)
|
138
|
+
git("branch -D #{branch_name}")
|
139
|
+
end
|
140
|
+
|
141
|
+
#
|
142
|
+
# push
|
143
|
+
#
|
144
|
+
|
145
|
+
def push(remote_name, ref)
|
146
|
+
git("push #{remote_name} #{ref}")
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
#
|
151
|
+
# remotes
|
152
|
+
#
|
153
|
+
|
154
|
+
def remotes(git_organization)
|
155
|
+
# returns {git_remote_name => project_name}
|
156
|
+
remotes = {}
|
157
|
+
git("remote -v").split("\n").each do |remote|
|
158
|
+
name, url, method = remote.split(/\s/)
|
159
|
+
if url =~ /^git@([\w\d\.]+):#{git_organization}\/[a-zA-Z0-9]+_([\w\d-]+)\.git$$/
|
160
|
+
remotes[name] = $2
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
remotes
|
165
|
+
end
|
166
|
+
|
167
|
+
def remote_add(name, url)
|
168
|
+
git("remote add #{name} #{url}")
|
169
|
+
end
|
170
|
+
|
171
|
+
#
|
172
|
+
# rev-parse
|
173
|
+
#
|
174
|
+
def git_ref(refname)
|
175
|
+
git("rev-parse --verify --quiet #{refname}")
|
176
|
+
end
|
177
|
+
|
178
|
+
#
|
179
|
+
# stash
|
180
|
+
#
|
181
|
+
|
182
|
+
def stash_working_dir(stash_description)
|
183
|
+
stash_output = git("stash save --include-untracked #{stash_description}")
|
184
|
+
did_stash_changes? stash_output
|
185
|
+
end
|
186
|
+
|
187
|
+
def did_stash_changes?(stash_message)
|
188
|
+
! (stash_message.include? "No local changes to save")
|
189
|
+
end
|
190
|
+
|
191
|
+
#
|
192
|
+
# status
|
193
|
+
#
|
194
|
+
|
195
|
+
def status
|
196
|
+
git('status --porcelain')
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
def has_commits?
|
201
|
+
# see http://stackoverflow.com/a/5492347
|
202
|
+
%x{ git rev-parse --verify --quiet HEAD }
|
203
|
+
$?.success?
|
204
|
+
end
|
205
|
+
|
206
|
+
def is_clean_working_directory?
|
207
|
+
status.empty?
|
208
|
+
end
|
209
|
+
|
210
|
+
# see https://www.kernel.org/pub/software/scm/git/docs/git-status.html#_output
|
211
|
+
GIT_STATUS_CODES__CONFLICT = Set.new ["DD", "AU", "UD", "UA", "DU", "AA", "UU"]
|
212
|
+
def has_conflicts?
|
213
|
+
def status_code(status_str)
|
214
|
+
status_str[0,2]
|
215
|
+
end
|
216
|
+
|
217
|
+
status_codes = status.split("\n").collect{|s| status_code(s)}
|
218
|
+
! GIT_STATUS_CODES__CONFLICT.intersection(status_codes).empty?
|
219
|
+
end
|
220
|
+
|
221
|
+
def untracked_files
|
222
|
+
git("ls-files -o --exclude-standard").split("\n")
|
223
|
+
end
|
224
|
+
|
225
|
+
#
|
226
|
+
# clone
|
227
|
+
#
|
228
|
+
def clone(git_url, path="")
|
229
|
+
git("clone %s \"%s\"" % [git_url, path], true, false)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|