web_git 0.0.4 → 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.
- checksums.yaml +4 -4
- data/Gemfile +6 -22
- data/Gemfile.lock +102 -177
- data/README.markdown +30 -4
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/lib/generators/web_git/install_generator.rb +47 -0
- data/lib/scripts/heroku_login.exp +58 -0
- data/lib/views/status.erb +381 -0
- data/lib/web_git/diff.rb +207 -206
- data/lib/web_git/exceptions.rb +3 -0
- data/lib/web_git/graph.rb +114 -0
- data/lib/web_git/heroku.rb +34 -0
- data/lib/web_git/string.rb +8 -0
- data/lib/web_git.rb +204 -9
- data/web_git.gemspec +30 -76
- metadata +30 -164
- data/ansi2html.sh +0 -514
- data/app/assets/javascripts/web_git/application.js +0 -22
- data/app/assets/javascripts/web_git/bootstrap.min.js +0 -6
- data/app/assets/javascripts/web_git/popper.min.js +0 -4
- data/app/assets/stylesheets/web_git/application.scss +0 -12
- data/app/assets/stylesheets/web_git/bootstrap.min.css +0 -6
- data/app/assets/stylesheets/web_git/font-awesome.min.css +0 -4
- data/app/assets/stylesheets/web_git/octicons.css +0 -5
- data/app/controllers/web_git/application_controller.rb +0 -5
- data/app/controllers/web_git/branches_controller.rb +0 -38
- data/app/controllers/web_git/commands_controller.rb +0 -51
- data/app/controllers/web_git/commits_controller.rb +0 -46
- data/app/views/layouts/web_git/application.html.erb +0 -39
- data/app/views/web_git/commands/status.html.erb +0 -244
- data/config/routes.rb +0 -10
- data/lib/web_git/engine.rb +0 -9
data/lib/web_git/diff.rb
CHANGED
@@ -1,233 +1,234 @@
|
|
1
|
-
module WebGit
|
2
|
-
class Diff
|
3
|
-
def self.get_diff
|
4
|
-
Dir.chdir(Rails.root) do
|
5
|
-
`git diff`
|
6
|
-
end
|
7
|
-
end
|
8
1
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
line_number
|
33
|
-
|
2
|
+
require "diffy"
|
3
|
+
require "git"
|
4
|
+
class Diff
|
5
|
+
|
6
|
+
def self.get_diff
|
7
|
+
# Dir.chdir(Rails.root) do
|
8
|
+
`git diff`
|
9
|
+
# end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.get_each_left(diff)
|
13
|
+
filenames = get_file_names("")
|
14
|
+
files = file_diffs(diff)
|
15
|
+
lefts = {}
|
16
|
+
files.each_with_index do |file, i|
|
17
|
+
file_content = ""
|
18
|
+
lines = file.split("\n").drop(4)
|
19
|
+
start_line = 0
|
20
|
+
current_line_index = 0
|
21
|
+
line_number = start_line + current_line_index
|
22
|
+
lines.each do |line|
|
23
|
+
if !line.match?(/@@ ([-]\d+,\d+\s[+]\d+,\d+) @@/)
|
24
|
+
if line[0] != "+"
|
25
|
+
file_content += "#{line_number}| " + line + "\n"
|
26
|
+
line_number += 1
|
34
27
|
end
|
35
|
-
|
36
|
-
|
37
|
-
end
|
38
|
-
lefts
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.get_each_right(diff)
|
42
|
-
filenames = get_file_names("")
|
43
|
-
files = file_diffs(diff)
|
44
|
-
rights = {}
|
45
|
-
files.each_with_index do |file, i|
|
46
|
-
file_content = ""
|
47
|
-
lines = file.split("\n").drop(4)
|
48
|
-
start_line = 0
|
49
|
-
current_line_index = 0
|
50
|
-
line_number = start_line + current_line_index
|
51
|
-
lines.each do |line|
|
28
|
+
else
|
29
|
+
current_line_index = 0
|
52
30
|
# The line numbers in the output of a git diff match this regex
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
else
|
60
|
-
current_line_index = 0
|
61
|
-
numbers = line.scan(/@@ ([-]\d+,\d+\s[+]\d+,\d+) @@/).map(&:join)
|
62
|
-
# If right, start line is the second in a split Array
|
63
|
-
start_line = numbers.first.split(" ").second.
|
64
|
-
split(",").first.to_i.abs
|
65
|
-
line_number = start_line + current_line_index
|
66
|
-
file_content += "\n"
|
67
|
-
end
|
31
|
+
numbers = line.scan(/@@ ([-]\d+,\d+\s[+]\d+,\d+) @@/).map(&:join)
|
32
|
+
# If left, starting line number is the first one in a split Array
|
33
|
+
start_line = numbers[0].split(" ")[0].
|
34
|
+
split(",")[0].to_i.abs
|
35
|
+
line_number = start_line + current_line_index
|
36
|
+
file_content += "\n"
|
68
37
|
end
|
69
|
-
rights[filenames[i]] = file_content.chomp "\n"
|
70
38
|
end
|
71
|
-
|
39
|
+
lefts[filenames[i]] = file_content.chomp "\n"
|
72
40
|
end
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
41
|
+
lefts
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.get_each_right(diff)
|
45
|
+
filenames = get_file_names("")
|
46
|
+
files = file_diffs(diff)
|
47
|
+
rights = {}
|
48
|
+
files.each_with_index do |file, i|
|
49
|
+
file_content = ""
|
50
|
+
lines = file.split("\n").drop(4)
|
51
|
+
start_line = 0
|
52
|
+
current_line_index = 0
|
53
|
+
line_number = start_line + current_line_index
|
54
|
+
lines.each do |line|
|
55
|
+
# The line numbers in the output of a git diff match this regex
|
56
|
+
# @@ -61,18 +61,15 @@
|
57
|
+
if !line.match?(/@@ ([-]\d+,\d+\s[+]\d+,\d+) @@/)
|
58
|
+
if line[0] != "-"
|
59
|
+
file_content += "#{line_number}| " + line + "\n"
|
60
|
+
line_number += 1
|
93
61
|
end
|
62
|
+
else
|
63
|
+
current_line_index = 0
|
64
|
+
numbers = line.scan(/@@ ([-]\d+,\d+\s[+]\d+,\d+) @@/).map(&:join)
|
65
|
+
# If right, start line is the second in a split Array
|
66
|
+
start_line = numbers[0].split(" ")[1].
|
67
|
+
split(",")[0].to_i.abs
|
68
|
+
line_number = start_line + current_line_index
|
69
|
+
file_content += "\n"
|
94
70
|
end
|
95
71
|
end
|
96
|
-
|
72
|
+
rights[filenames[i]] = file_content.chomp "\n"
|
97
73
|
end
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
74
|
+
rights
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.get_last_commit_hash
|
78
|
+
working_dir = Dir.pwd
|
79
|
+
git = Git.open(working_dir)
|
80
|
+
git.log.first.sha.slice(0, 7)
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.file_diffs(diff)
|
84
|
+
diff.scan(/diff --git.*?(?=diff --git|\z)/m)
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.match_other_files(line, file, filenames)
|
88
|
+
filenames.each do |other_file|
|
89
|
+
if file != other_file
|
90
|
+
# It looks like:
|
91
|
+
# --- a/<path-to-file>
|
92
|
+
# +++ b/<path-to-file>
|
93
|
+
if line.include?('diff --git a/' + other_file + ' b/' + other_file)
|
94
|
+
return true
|
105
95
|
end
|
106
|
-
filenames.split("\n")
|
107
96
|
end
|
108
97
|
end
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
98
|
+
false
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.get_file_names(commit)
|
102
|
+
git = Git.open(Dir.pwd)
|
103
|
+
if commit.empty?
|
104
|
+
filenames = git.status.changed.keys
|
105
|
+
else
|
106
|
+
filenames = git.diff(commit, "HEAD").map(&:path)
|
114
107
|
end
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
108
|
+
filenames
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.get_last_diff
|
112
|
+
# Dir.chdir(Rails.root) do
|
113
|
+
`git diff -M HEAD~1`
|
114
|
+
# end
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.get_last_left(diff)
|
118
|
+
filenames = get_file_names("HEAD~1")
|
119
|
+
files = file_diffs(diff)
|
120
|
+
ones = {}
|
121
|
+
files.each_with_index do |file, i|
|
122
|
+
file_content = ""
|
123
|
+
lines = file.split("\n").drop(4)
|
124
|
+
lines.each do |line|
|
125
|
+
# The line numbers in the output of a git diff match this regex
|
126
|
+
# @@ -61,18 +61,15 @@
|
127
|
+
if !line.match?(/@@ ([-]\d+,\d+\s[+]\d+,\d+) @@/)
|
128
|
+
if line[0] != "+"
|
129
|
+
line.slice!(0)
|
130
|
+
file_content += line + "\n"
|
133
131
|
end
|
132
|
+
else
|
133
|
+
file_content += "\n"
|
134
134
|
end
|
135
|
-
ones[filenames[i]] = file_content.chomp "\n"
|
136
135
|
end
|
137
|
-
ones
|
136
|
+
ones[filenames[i]] = file_content.chomp "\n"
|
138
137
|
end
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
file_content += "\n"
|
138
|
+
ones
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.get_last_right(diff)
|
142
|
+
filenames = get_file_names("HEAD~1")
|
143
|
+
files = file_diffs(diff)
|
144
|
+
ones = {}
|
145
|
+
files.each_with_index do |file, i|
|
146
|
+
file_content = ""
|
147
|
+
lines = file.split("\n").drop(4)
|
148
|
+
lines.each do |line|
|
149
|
+
if !line.match?(/@@ ([-]\d+,\d+\s[+]\d+,\d+) @@/)
|
150
|
+
if line[0] != "+"
|
151
|
+
elsif line[0] == "+"
|
152
|
+
line.slice!(0)
|
153
|
+
file_content += line + "\n"
|
156
154
|
end
|
155
|
+
else
|
156
|
+
file_content += "\n"
|
157
157
|
end
|
158
|
-
ones[filenames[i]] = file_content.chomp "\n"
|
159
158
|
end
|
160
|
-
ones
|
159
|
+
ones[filenames[i]] = file_content.chomp "\n"
|
161
160
|
end
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
161
|
+
ones
|
162
|
+
end
|
163
|
+
|
164
|
+
def self.last_to_html(diff)
|
165
|
+
left_hash = get_last_left(diff)
|
166
|
+
right_hash = get_last_right(diff)
|
167
|
+
|
168
|
+
html_output = '<link rel="stylesheet"' +
|
169
|
+
'href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/' +
|
170
|
+
'bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/' +
|
171
|
+
'1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">'
|
172
|
+
html_output += '<style>'
|
173
|
+
html_output += Diffy::CSS
|
174
|
+
html_output += '</style>'
|
175
|
+
html_output += '<div class="card" style="overflow-y: scroll;max-height:400px">'
|
176
|
+
left_hash.keys.each do |file|
|
177
|
+
html_output += '<div class="file mb-4 p-1">'
|
178
|
+
html_output += '<h4>' + file + '</h4>'
|
179
|
+
html_output += Diffy::Diff.new(
|
180
|
+
left_hash[file],
|
181
|
+
right_hash[file],
|
182
|
+
:include_plus_and_minus_in_html => true,
|
183
|
+
:allow_empty_diff => false
|
184
|
+
).to_s(:html)
|
185
185
|
html_output += '</div>'
|
186
|
-
html_output
|
187
186
|
end
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
187
|
+
html_output += '</div>'
|
188
|
+
html_output
|
189
|
+
end
|
190
|
+
|
191
|
+
def self.diff_to_html(diff)
|
192
|
+
left_hash = get_each_left(diff)
|
193
|
+
right_hash = get_each_right(diff)
|
194
|
+
html_output = '<div style="overflow-y: scroll;height:400px">'
|
195
|
+
html_output += '<style>'
|
196
|
+
html_output += Diffy::CSS
|
197
|
+
html_output += '</style>'
|
198
|
+
html_output += '<div class="row mb-3">'
|
199
|
+
html_output += '<div class="col-md-12 offset" style="overflow-y: scroll;">'
|
200
|
+
|
201
|
+
left_hash.keys.each do |file|
|
202
|
+
html_output += '<div class="row text-center">
|
203
|
+
<div class="col-12">
|
204
|
+
<h4>'
|
205
|
+
html_output+= file.to_s
|
206
|
+
html_output += '</h4>
|
206
207
|
</div>
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
html_output += "</div>"
|
227
|
-
html_output += "</div>"
|
228
|
-
html_output += "</div>"
|
229
|
-
html_output
|
230
|
-
|
208
|
+
</div>
|
209
|
+
<div class="row mb-4">
|
210
|
+
<div class="col-6">'
|
211
|
+
html_output += Diffy::SplitDiff.new(
|
212
|
+
left_hash[file],
|
213
|
+
right_hash[file],
|
214
|
+
:format => :html
|
215
|
+
).left
|
216
|
+
|
217
|
+
html_output +=
|
218
|
+
'</div>
|
219
|
+
<div class="col-6">'
|
220
|
+
html_output += Diffy::SplitDiff.new(
|
221
|
+
left_hash[file],
|
222
|
+
right_hash[file],
|
223
|
+
:format => :html
|
224
|
+
).right
|
225
|
+
|
226
|
+
html_output += '</div></div>'
|
231
227
|
end
|
228
|
+
html_output += "</div>"
|
229
|
+
html_output += "</div>"
|
230
|
+
html_output += "</div>"
|
231
|
+
html_output
|
232
|
+
|
233
|
+
end
|
232
234
|
end
|
233
|
-
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module WebGit
|
2
|
+
require "git"
|
3
|
+
require "ansispan"
|
4
|
+
|
5
|
+
class Graph
|
6
|
+
require "action_view"
|
7
|
+
require "action_view/helpers"
|
8
|
+
include ActionView::Helpers::DateHelper
|
9
|
+
attr_accessor :heads
|
10
|
+
|
11
|
+
def initialize(git)
|
12
|
+
@git = git
|
13
|
+
@full_list = []
|
14
|
+
@heads = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_hash
|
18
|
+
|
19
|
+
has_changes = has_untracked_changes?
|
20
|
+
if has_changes
|
21
|
+
temporarily_stash_changes
|
22
|
+
end
|
23
|
+
|
24
|
+
draw_graph
|
25
|
+
|
26
|
+
if has_changes
|
27
|
+
stash_pop
|
28
|
+
end
|
29
|
+
@full_list
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.project_root
|
33
|
+
if defined?(Rails) && Rails.respond_to?("root")
|
34
|
+
return Rails.root
|
35
|
+
end
|
36
|
+
|
37
|
+
if defined?(Bundler)
|
38
|
+
return Bundler.root
|
39
|
+
end
|
40
|
+
|
41
|
+
Dir.pwd
|
42
|
+
end
|
43
|
+
|
44
|
+
def cli_graph
|
45
|
+
Dir.chdir(Graph.project_root) do
|
46
|
+
@cli_graph = `git log --oneline --decorate --graph --all --color`
|
47
|
+
all_commits = `git log --all --format=format:%H`.split("\n").map{|a| a.slice(0,7)}
|
48
|
+
|
49
|
+
@cli_graph = Ansispan.convert(@cli_graph)
|
50
|
+
all_commits.each do |sha|
|
51
|
+
sha_button = "<span class=\"commit\"><button class=\"btn btn-link sha\">#{sha}</button></span>"
|
52
|
+
@cli_graph.gsub!(sha, sha_button)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
@cli_graph
|
56
|
+
end
|
57
|
+
|
58
|
+
def has_untracked_changes?
|
59
|
+
@git.diff.size > 0
|
60
|
+
end
|
61
|
+
|
62
|
+
def temporarily_stash_changes
|
63
|
+
@git.add(all: true)
|
64
|
+
stash_count = Git::Stashes.new(@git).count
|
65
|
+
Git::Stash.new(@git, "Temporary Stash #{stash_count}")
|
66
|
+
end
|
67
|
+
|
68
|
+
def stash_pop
|
69
|
+
stashes = Git::Stashes.new(@git)
|
70
|
+
stashes.apply(0)
|
71
|
+
end
|
72
|
+
|
73
|
+
def draw_graph
|
74
|
+
starting_branch = @git.current_branch
|
75
|
+
branches = @git.branches.local.map(&:name)
|
76
|
+
branches.each do |branch_name|
|
77
|
+
branch = { branch: branch_name }
|
78
|
+
@git.checkout(branch_name)
|
79
|
+
log_commits = build_array_of_commit_hashes
|
80
|
+
branch[:log] = log_commits
|
81
|
+
branch[:head] = log_commits.last[:sha]
|
82
|
+
@full_list.push branch
|
83
|
+
end
|
84
|
+
@git.checkout(starting_branch)
|
85
|
+
|
86
|
+
@full_list.each do |branch_hash|
|
87
|
+
head_sha = branch_hash[:head]
|
88
|
+
branch_name = branch_hash[:branch]
|
89
|
+
|
90
|
+
if @heads[head_sha].nil?
|
91
|
+
@heads[head_sha] = [branch_name]
|
92
|
+
else
|
93
|
+
@heads[head_sha].push branch_name
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
def build_array_of_commit_hashes
|
100
|
+
log_commits = []
|
101
|
+
@git.log.sort_by(&:date).each do |git_commit_object|
|
102
|
+
commit = {}
|
103
|
+
commit[:sha] = git_commit_object.sha.slice(0..7)
|
104
|
+
commit[:date] = git_commit_object.date
|
105
|
+
commit[:formatted_date] = time_ago_in_words(git_commit_object.date)
|
106
|
+
commit[:message] = git_commit_object.message
|
107
|
+
commit[:author] = git_commit_object.author.name
|
108
|
+
commit[:heads] = []
|
109
|
+
log_commits.push commit
|
110
|
+
end
|
111
|
+
log_commits
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'web_git/exceptions'
|
3
|
+
|
4
|
+
module WebGit
|
5
|
+
class Heroku
|
6
|
+
def self.authenticate(email, password)
|
7
|
+
raise ArgumentError.new("Email and password cannot be blank.") if email.blank? || password.blank?
|
8
|
+
script = File.join( File.dirname(__FILE__), '/../scripts/heroku_login.exp')
|
9
|
+
|
10
|
+
command = "#{script} #{email} #{password}"
|
11
|
+
rout, wout = IO.pipe
|
12
|
+
pid = Process.spawn(command, :out => wout)
|
13
|
+
|
14
|
+
begin
|
15
|
+
status = Timeout.timeout(30) do
|
16
|
+
_, status = Process.wait2(pid)
|
17
|
+
wout.close
|
18
|
+
end
|
19
|
+
stdout = rout.readlines.join("\n")
|
20
|
+
rout.close
|
21
|
+
message = stdout.match(/Error.*\./).to_s
|
22
|
+
raise WebGit::AuthenticationError.new(message) if stdout.include?("Error")
|
23
|
+
|
24
|
+
rescue Timeout::Error
|
25
|
+
Process.kill('TERM', script_pid)
|
26
|
+
raise Timeout::Error.new("Sign in took longer than 30 seconds.")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.whoami
|
31
|
+
`heroku whoami`.chomp
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|