leg 0.0.1 → 0.0.2
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 +5 -5
- data/.gitignore +10 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +34 -0
- data/LICENSE +21 -0
- data/README.md +59 -0
- data/Rakefile +11 -0
- data/TUTORIAL.md +243 -0
- data/bin/console +9 -0
- data/bin/setup +6 -0
- data/exe/leg +6 -0
- data/leg.gemspec +31 -0
- data/lib/leg.rb +27 -0
- data/lib/leg/cli.rb +42 -0
- data/lib/leg/commands.rb +19 -0
- data/lib/leg/commands/amend.rb +49 -0
- data/lib/leg/commands/base_command.rb +99 -0
- data/lib/leg/commands/build.rb +126 -0
- data/lib/leg/commands/commit.rb +48 -0
- data/lib/leg/commands/diff.rb +29 -0
- data/lib/leg/commands/help.rb +43 -0
- data/lib/leg/commands/init.rb +46 -0
- data/lib/leg/commands/reset.rb +26 -0
- data/lib/leg/commands/resolve.rb +31 -0
- data/lib/leg/commands/save.rb +31 -0
- data/lib/leg/commands/status.rb +54 -0
- data/lib/leg/commands/step.rb +31 -0
- data/lib/leg/config.rb +42 -0
- data/lib/leg/default_templates.rb +340 -0
- data/lib/leg/diff.rb +151 -0
- data/lib/leg/diff_transformers.rb +11 -0
- data/lib/leg/diff_transformers/base_transformer.rb +13 -0
- data/lib/leg/diff_transformers/fold_sections.rb +89 -0
- data/lib/leg/diff_transformers/omit_adjacent_removals.rb +38 -0
- data/lib/leg/diff_transformers/syntax_highlight.rb +32 -0
- data/lib/leg/diff_transformers/trim_blank_lines.rb +25 -0
- data/lib/leg/line.rb +83 -0
- data/lib/leg/markdown.rb +20 -0
- data/lib/leg/page.rb +27 -0
- data/lib/leg/representations.rb +9 -0
- data/lib/leg/representations/base_representation.rb +42 -0
- data/lib/leg/representations/git.rb +388 -0
- data/lib/leg/representations/litdiff.rb +85 -0
- data/lib/leg/step.rb +16 -0
- data/lib/leg/template.rb +95 -0
- data/lib/leg/tutorial.rb +49 -0
- data/lib/leg/version.rb +3 -0
- metadata +112 -38
- data/bin/leg +0 -9
- data/lib/snaptoken.rb +0 -24
- data/lib/snaptoken/cli.rb +0 -61
- data/lib/snaptoken/commands.rb +0 -13
- data/lib/snaptoken/commands/amend.rb +0 -27
- data/lib/snaptoken/commands/base_command.rb +0 -92
- data/lib/snaptoken/commands/build.rb +0 -107
- data/lib/snaptoken/commands/commit.rb +0 -27
- data/lib/snaptoken/commands/help.rb +0 -38
- data/lib/snaptoken/commands/resolve.rb +0 -27
- data/lib/snaptoken/commands/status.rb +0 -21
- data/lib/snaptoken/commands/step.rb +0 -35
- data/lib/snaptoken/default_templates.rb +0 -287
- data/lib/snaptoken/diff.rb +0 -180
- data/lib/snaptoken/diff_line.rb +0 -54
- data/lib/snaptoken/diff_transformers.rb +0 -9
- data/lib/snaptoken/diff_transformers/base_transformer.rb +0 -9
- data/lib/snaptoken/diff_transformers/fold_sections.rb +0 -85
- data/lib/snaptoken/diff_transformers/omit_adjacent_removals.rb +0 -28
- data/lib/snaptoken/diff_transformers/trim_blank_lines.rb +0 -21
- data/lib/snaptoken/markdown.rb +0 -18
- data/lib/snaptoken/page.rb +0 -64
- data/lib/snaptoken/representations.rb +0 -8
- data/lib/snaptoken/representations/base_representation.rb +0 -38
- data/lib/snaptoken/representations/git.rb +0 -262
- data/lib/snaptoken/representations/litdiff.rb +0 -81
- data/lib/snaptoken/step.rb +0 -27
- data/lib/snaptoken/template.rb +0 -53
- data/lib/snaptoken/tutorial.rb +0 -64
data/lib/snaptoken/diff_line.rb
DELETED
@@ -1,54 +0,0 @@
|
|
1
|
-
class Snaptoken::DiffLine
|
2
|
-
TYPES = [:added, :removed, :unchanged, :folded]
|
3
|
-
|
4
|
-
attr_reader :type, :source, :line_numbers
|
5
|
-
attr_writer :source, :line_numbers
|
6
|
-
|
7
|
-
def initialize(type, source, line_numbers)
|
8
|
-
unless TYPES.include? type
|
9
|
-
raise ArgumentError, "type must be one of: #{TYPES.inspect}"
|
10
|
-
end
|
11
|
-
@type = type
|
12
|
-
@source = source.chomp
|
13
|
-
@line_numbers = line_numbers
|
14
|
-
end
|
15
|
-
|
16
|
-
def clone
|
17
|
-
Snaptoken::DiffLine.new(@type, @source.dup, @line_numbers.dup)
|
18
|
-
end
|
19
|
-
|
20
|
-
def type=(type)
|
21
|
-
unless TYPES.include? type
|
22
|
-
raise ArgumentError, "type must be one of: #{TYPES.inspect}"
|
23
|
-
end
|
24
|
-
@type = type
|
25
|
-
end
|
26
|
-
|
27
|
-
def blank?
|
28
|
-
@source.strip.empty?
|
29
|
-
end
|
30
|
-
|
31
|
-
def line_number
|
32
|
-
case @type
|
33
|
-
when :removed, :folded
|
34
|
-
@line_numbers[0]
|
35
|
-
when :added, :unchanged
|
36
|
-
@line_numbers[1]
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def to_patch(options = {})
|
41
|
-
options[:unchanged_char] ||= " "
|
42
|
-
|
43
|
-
case @type
|
44
|
-
when :added
|
45
|
-
"+#{@source}\n"
|
46
|
-
when :removed
|
47
|
-
"-#{@source}\n"
|
48
|
-
when :unchanged
|
49
|
-
"#{options[:unchanged_char]}#{@source}\n"
|
50
|
-
when :folded
|
51
|
-
raise "can't convert folded line to patch"
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
@@ -1,9 +0,0 @@
|
|
1
|
-
module Snaptoken::DiffTransformers
|
2
|
-
end
|
3
|
-
|
4
|
-
require 'snaptoken/diff_transformers/base_transformer'
|
5
|
-
|
6
|
-
require 'snaptoken/diff_transformers/fold_sections'
|
7
|
-
require 'snaptoken/diff_transformers/omit_adjacent_removals'
|
8
|
-
require 'snaptoken/diff_transformers/trim_blank_lines'
|
9
|
-
|
@@ -1,85 +0,0 @@
|
|
1
|
-
class Snaptoken::DiffTransformers::FoldSections < Snaptoken::DiffTransformers::BaseTransformer
|
2
|
-
def transform(diff)
|
3
|
-
sections = @options[:section_types].map { [] }
|
4
|
-
|
5
|
-
cur_sections = @options[:section_types].map { nil }
|
6
|
-
diff.lines.each.with_index do |line, idx|
|
7
|
-
@options[:section_types].each.with_index do |section_type, level|
|
8
|
-
if line.source =~ Regexp.new(section_type[:start])
|
9
|
-
if !section_type[:end] && cur_sections[level]
|
10
|
-
cur_sections[level].end_line = idx - 1
|
11
|
-
if @options[:unfold_before_new_section]
|
12
|
-
cur_sections[level].dirty! if [:added, :removed].include? line.type
|
13
|
-
end
|
14
|
-
sections[level] << cur_sections[level]
|
15
|
-
end
|
16
|
-
|
17
|
-
cur_sections[level] = Section.new(level, idx)
|
18
|
-
|
19
|
-
if [:added, :removed].include? line.type
|
20
|
-
cur_sections[level].dirty!
|
21
|
-
end
|
22
|
-
elsif section_type[:end] && line.source =~ Regexp.new(section_type[:end])
|
23
|
-
if [:added, :removed].include? line.type
|
24
|
-
cur_sections[level].dirty!
|
25
|
-
end
|
26
|
-
|
27
|
-
cur_sections[level].end_line = idx
|
28
|
-
sections[level] << cur_sections[level]
|
29
|
-
cur_sections[level] = nil
|
30
|
-
elsif cur_sections[level]
|
31
|
-
if [:added, :removed].include? line.type
|
32
|
-
cur_sections[level].dirty!
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
cur_sections.each.with_index do |section, level|
|
38
|
-
unless section.nil?
|
39
|
-
section.end_line = diff.lines.length - 1
|
40
|
-
sections[level] << section
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
new_diff = diff.clone
|
45
|
-
sections.each.with_index do |level_sections, level|
|
46
|
-
level_sections.each do |section|
|
47
|
-
if !section.dirty? && !new_diff.lines[section.to_range].any?(&:nil?)
|
48
|
-
start_line = new_diff.lines[section.start_line]
|
49
|
-
end_line = new_diff.lines[section.end_line]
|
50
|
-
|
51
|
-
summary_lines = [start_line]
|
52
|
-
summary_lines << end_line if @options[:section_types][level][:end]
|
53
|
-
summary = summary_lines.map(&:source).join(" … ")
|
54
|
-
|
55
|
-
line_numbers = [start_line.line_number, end_line.line_number]
|
56
|
-
|
57
|
-
folded_line = Snaptoken::DiffLine.new(:folded, summary, line_numbers)
|
58
|
-
|
59
|
-
section.to_range.each do |idx|
|
60
|
-
new_diff.lines[idx] = nil
|
61
|
-
end
|
62
|
-
|
63
|
-
new_diff.lines[section.start_line] = folded_line
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
new_diff.lines.compact!
|
68
|
-
new_diff
|
69
|
-
end
|
70
|
-
|
71
|
-
class Section
|
72
|
-
attr_accessor :level, :start_line, :end_line, :dirty
|
73
|
-
|
74
|
-
def initialize(level, start_line, end_line = nil, dirty = false)
|
75
|
-
@level, @start_line, @end_line, @dirty = level, start_line, end_line, dirty
|
76
|
-
end
|
77
|
-
|
78
|
-
def to_range
|
79
|
-
start_line..end_line
|
80
|
-
end
|
81
|
-
|
82
|
-
def dirty?; @dirty; end
|
83
|
-
def dirty!; @dirty = true; end
|
84
|
-
end
|
85
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
class Snaptoken::DiffTransformers::OmitAdjacentRemovals < Snaptoken::DiffTransformers::BaseTransformer
|
2
|
-
def transform(diff)
|
3
|
-
new_diff = diff.clone
|
4
|
-
|
5
|
-
removed_lines = []
|
6
|
-
saw_added_line = false
|
7
|
-
new_diff.lines.each.with_index do |line, idx|
|
8
|
-
case line.type
|
9
|
-
when :unchanged, :folded
|
10
|
-
if saw_added_line
|
11
|
-
removed_lines.each do |removed_idx|
|
12
|
-
new_diff.lines[removed_idx] = nil
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
removed_lines = []
|
17
|
-
saw_added_line = false
|
18
|
-
when :added
|
19
|
-
saw_added_line = true
|
20
|
-
when :removed
|
21
|
-
removed_lines << idx
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
new_diff.lines.compact!
|
26
|
-
new_diff
|
27
|
-
end
|
28
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
class Snaptoken::DiffTransformers::TrimBlankLines < Snaptoken::DiffTransformers::BaseTransformer
|
2
|
-
def transform(diff)
|
3
|
-
new_diff = diff.clone_empty
|
4
|
-
diff.lines.each.with_index do |line, idx|
|
5
|
-
line = line.clone
|
6
|
-
if line.blank? && [:added, :removed].include?(line.type)
|
7
|
-
prev_line = idx > 0 ? diff.lines[idx - 1] : nil
|
8
|
-
next_line = idx < diff.lines.length - 1 ? diff.lines[idx + 1] : nil
|
9
|
-
|
10
|
-
prev_changed = prev_line && [:added, :removed].include?(prev_line.type)
|
11
|
-
next_changed = next_line && [:added, :removed].include?(next_line.type)
|
12
|
-
|
13
|
-
if !prev_changed || !next_changed
|
14
|
-
line.type = :unchanged
|
15
|
-
end
|
16
|
-
end
|
17
|
-
new_diff.lines << line
|
18
|
-
end
|
19
|
-
new_diff
|
20
|
-
end
|
21
|
-
end
|
data/lib/snaptoken/markdown.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
module Snaptoken::Markdown
|
2
|
-
class HTMLRouge < Redcarpet::Render::HTML
|
3
|
-
include Rouge::Plugins::Redcarpet
|
4
|
-
end
|
5
|
-
|
6
|
-
HTML_RENDERER = HTMLRouge.new(with_toc_data: true)
|
7
|
-
MARKDOWN_RENDERER = Redcarpet::Markdown.new(HTML_RENDERER, fenced_code_blocks: true)
|
8
|
-
|
9
|
-
def self.render(source)
|
10
|
-
html = MARKDOWN_RENDERER.render(source)
|
11
|
-
html = Redcarpet::Render::SmartyPants.render(html)
|
12
|
-
html.gsub!(/<\/code>‘/) { "</code>’" }
|
13
|
-
html.gsub!(/^\s*<h([23456]) id="([^"]+)">(.+)<\/h\d>$/) {
|
14
|
-
"<h#{$1} id=\"#{$2}\"><a href=\"##{$2}\">#{$3}</a></h#{$1}>"
|
15
|
-
}
|
16
|
-
html
|
17
|
-
end
|
18
|
-
end
|
data/lib/snaptoken/page.rb
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
class Snaptoken::Page
|
2
|
-
attr_accessor :filename, :steps, :footer_text
|
3
|
-
|
4
|
-
def initialize(filename = "tutorial")
|
5
|
-
@filename = filename
|
6
|
-
@steps = []
|
7
|
-
@footer_text = nil
|
8
|
-
end
|
9
|
-
|
10
|
-
def <<(step)
|
11
|
-
@steps << step
|
12
|
-
self
|
13
|
-
end
|
14
|
-
|
15
|
-
def empty?
|
16
|
-
@steps.empty?
|
17
|
-
end
|
18
|
-
|
19
|
-
def title
|
20
|
-
first_line = @steps.first ? @steps.first.text.lines.first : (@footer_text ? @footer_text.lines.first : nil)
|
21
|
-
if first_line && first_line.start_with?("# ")
|
22
|
-
first_line[2..-1].strip
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def to_html(tutorial, offline)
|
27
|
-
content = ""
|
28
|
-
@steps.each do |step|
|
29
|
-
if !step.text.strip.empty?
|
30
|
-
html = Snaptoken::Markdown.render(step.text)
|
31
|
-
html.gsub!(/<p>{{step (\d+)}}<\/p>/) do
|
32
|
-
step = tutorial.step($1.to_i)
|
33
|
-
step.syntax_highlight!
|
34
|
-
step.to_html(tutorial, offline)
|
35
|
-
end
|
36
|
-
content << html
|
37
|
-
end
|
38
|
-
|
39
|
-
step.syntax_highlight!
|
40
|
-
content << step.to_html(tutorial, offline)
|
41
|
-
end
|
42
|
-
if @footer_text
|
43
|
-
# TODO: DRY this up. Please.
|
44
|
-
html = Snaptoken::Markdown.render(@footer_text)
|
45
|
-
html.gsub!(/<p>{{step (\d+)}}<\/p>/) do
|
46
|
-
step = tutorial.step($1.to_i)
|
47
|
-
step.syntax_highlight!
|
48
|
-
step.to_html(tutorial, offline)
|
49
|
-
end
|
50
|
-
content << html
|
51
|
-
end
|
52
|
-
|
53
|
-
page_number = tutorial.pages.index(self) + 1
|
54
|
-
|
55
|
-
Snaptoken::Template.new(tutorial.page_template, tutorial,
|
56
|
-
offline: offline,
|
57
|
-
page_title: title,
|
58
|
-
content: content,
|
59
|
-
page_number: page_number,
|
60
|
-
prev_page: page_number > 1 ? tutorial.pages[page_number - 2] : nil,
|
61
|
-
next_page: page_number < tutorial.pages.length ? tutorial.pages[page_number] : nil
|
62
|
-
).render_template
|
63
|
-
end
|
64
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
class Snaptoken::Representations::BaseRepresentation
|
2
|
-
def initialize(tutorial)
|
3
|
-
@tutorial = tutorial
|
4
|
-
end
|
5
|
-
|
6
|
-
# Should save @tutorial to disk.
|
7
|
-
def save!(options = {})
|
8
|
-
raise NotImplementedError
|
9
|
-
end
|
10
|
-
|
11
|
-
# Should load @tutorial (in place) from disk, and return it.
|
12
|
-
def load!(options = {})
|
13
|
-
raise NotImplementedError
|
14
|
-
end
|
15
|
-
|
16
|
-
# Returns true if this representation has been modified by the user since the
|
17
|
-
# last sync.
|
18
|
-
def modified?
|
19
|
-
synced_at = @tutorial.last_synced_at
|
20
|
-
repr_modified_at = modified_at
|
21
|
-
return false if synced_at.nil? or repr_modified_at.nil?
|
22
|
-
|
23
|
-
repr_modified_at > synced_at
|
24
|
-
end
|
25
|
-
|
26
|
-
# Returns true if this representation currently exists on disk.
|
27
|
-
def exists?
|
28
|
-
!modified_at.nil?
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
# Should return the Time the representation on disk was last modified, or nil
|
34
|
-
# if the representation doesn't exist.
|
35
|
-
def modified_at
|
36
|
-
raise NotImplementedError
|
37
|
-
end
|
38
|
-
end
|
@@ -1,262 +0,0 @@
|
|
1
|
-
class Snaptoken::Representations::Git < Snaptoken::Representations::BaseRepresentation
|
2
|
-
def save!(options = {})
|
3
|
-
FileUtils.rm_rf(repo_path)
|
4
|
-
FileUtils.mkdir_p(repo_path)
|
5
|
-
|
6
|
-
FileUtils.cd(repo_path) do
|
7
|
-
repo = Rugged::Repository.init_at(".")
|
8
|
-
|
9
|
-
step_num = 1
|
10
|
-
@tutorial.pages.each do |page|
|
11
|
-
message = "~~~ #{page.filename}"
|
12
|
-
message << "\n\n#{page.footer_text}" if page.footer_text
|
13
|
-
add_commit(repo, nil, message, step_num)
|
14
|
-
page.steps.each do |step|
|
15
|
-
message = "#{step.summary}\n\n#{step.text}".strip
|
16
|
-
add_commit(repo, step.to_patch, message, step_num)
|
17
|
-
|
18
|
-
yield step_num if block_given?
|
19
|
-
step_num += 1
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
#if options[:extra_path]
|
24
|
-
# FileUtils.cp_r(File.join(options[:extra_path], "."), ".")
|
25
|
-
# add_commit(repo, nil, "-", step_num, counter)
|
26
|
-
#end
|
27
|
-
|
28
|
-
repo.checkout_head(strategy: :force)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# Options:
|
33
|
-
# full_diffs: If true, diffs contain the entire file in one hunk instead of
|
34
|
-
# multiple contextual hunks.
|
35
|
-
# diffs_ignore_whitespace: If true, diffs don't show changes to lines when
|
36
|
-
# only the amount of whitespace is changed.
|
37
|
-
def load!(options = {})
|
38
|
-
git_diff_options = {}
|
39
|
-
git_diff_options[:context_lines] = 100_000 if options[:full_diffs]
|
40
|
-
git_diff_options[:ignore_whitespace_change] = true if options[:diffs_ignore_whitespace]
|
41
|
-
|
42
|
-
page = nil
|
43
|
-
@tutorial.clear
|
44
|
-
each_step(git_diff_options) do |step_num, commit, summary, text, patches|
|
45
|
-
if patches.empty?
|
46
|
-
if summary =~ /^~~~ (.+)$/
|
47
|
-
@tutorial << page unless page.nil?
|
48
|
-
|
49
|
-
page = Snaptoken::Page.new($1)
|
50
|
-
page.footer_text = text unless text.empty?
|
51
|
-
else
|
52
|
-
puts "Warning: ignoring empty commit."
|
53
|
-
end
|
54
|
-
else
|
55
|
-
patch = patches.map(&:to_s).join("\n")
|
56
|
-
step_diffs = Snaptoken::Diff.parse(patch)
|
57
|
-
|
58
|
-
page ||= Snaptoken::Page.new
|
59
|
-
page << Snaptoken::Step.new(step_num, summary, text, step_diffs)
|
60
|
-
|
61
|
-
yield step_num if block_given?
|
62
|
-
end
|
63
|
-
end
|
64
|
-
@tutorial << page unless page.nil?
|
65
|
-
@tutorial
|
66
|
-
end
|
67
|
-
|
68
|
-
def copy_repo_to_step!
|
69
|
-
FileUtils.mkdir_p(step_path)
|
70
|
-
FileUtils.rm_rf(File.join(step_path, "."), secure: true)
|
71
|
-
FileUtils.cd(repo_path) do
|
72
|
-
files = Dir.glob("*", File::FNM_DOTMATCH) - [".", "..", ".git"]
|
73
|
-
files.each do |f|
|
74
|
-
FileUtils.cp_r(f, File.join(step_path, f))
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def copy_step_to_repo!
|
80
|
-
FileUtils.mv(
|
81
|
-
File.join(repo_path, ".git"),
|
82
|
-
File.join(repo_path, "../.gittemp")
|
83
|
-
)
|
84
|
-
FileUtils.rm_rf(File.join(repo_path, "."), secure: true)
|
85
|
-
FileUtils.mv(
|
86
|
-
File.join(repo_path, "../.gittemp"),
|
87
|
-
File.join(repo_path, ".git")
|
88
|
-
)
|
89
|
-
FileUtils.cd(step_path) do
|
90
|
-
files = Dir.glob("*", File::FNM_DOTMATCH) - [".", ".."]
|
91
|
-
files.each do |f|
|
92
|
-
FileUtils.cp_r(f, File.join(repo_path, f))
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def repo_path
|
98
|
-
File.join(@tutorial.config[:path], ".leg/repo")
|
99
|
-
end
|
100
|
-
|
101
|
-
def repo
|
102
|
-
@repo ||= Rugged::Repository.new(repo_path)
|
103
|
-
end
|
104
|
-
|
105
|
-
def each_commit(options = {})
|
106
|
-
walker = Rugged::Walker.new(repo)
|
107
|
-
walker.sorting(Rugged::SORT_TOPO | Rugged::SORT_REVERSE)
|
108
|
-
|
109
|
-
master_commit = repo.branches["master"].target
|
110
|
-
walker.push(master_commit)
|
111
|
-
|
112
|
-
return [] if master_commit.oid == options[:after]
|
113
|
-
walker.hide(options[:after]) if options[:after]
|
114
|
-
|
115
|
-
return walker.to_a if not block_given?
|
116
|
-
|
117
|
-
walker.each do |commit|
|
118
|
-
yield commit
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
alias_method :commits, :each_commit
|
123
|
-
|
124
|
-
def each_step(git_diff_options = {})
|
125
|
-
empty_tree = Rugged::Tree.empty(repo)
|
126
|
-
step_num = 1
|
127
|
-
each_commit do |commit|
|
128
|
-
commit_message = commit.message.strip
|
129
|
-
summary = commit_message.lines.first.strip
|
130
|
-
text = (commit_message.lines[2..-1] || []).join.strip
|
131
|
-
next if commit_message == "-"
|
132
|
-
commit_message = "" if commit_message == "~"
|
133
|
-
last_commit = commit.parents.first
|
134
|
-
diff = (last_commit || empty_tree).diff(commit, git_diff_options)
|
135
|
-
patches = diff.each_patch.to_a
|
136
|
-
|
137
|
-
if patches.empty?
|
138
|
-
yield nil, commit, summary, text, patches
|
139
|
-
else
|
140
|
-
yield step_num, commit, summary, text, patches
|
141
|
-
step_num += 1
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
def commit!(options = {})
|
147
|
-
copy_step_to_repo!
|
148
|
-
remaining_commits = commits(after: repo.head.target).map(&:oid)
|
149
|
-
FileUtils.cd(repo_path) do
|
150
|
-
`git add -A`
|
151
|
-
`git commit #{'--amend' if options[:amend]} -m"TODO: let user specify commit message"`
|
152
|
-
end
|
153
|
-
rebase!(remaining_commits)
|
154
|
-
end
|
155
|
-
|
156
|
-
def resolve!
|
157
|
-
copy_step_to_repo!
|
158
|
-
FileUtils.cd(repo_path) do
|
159
|
-
`git add -A`
|
160
|
-
`git -c core.editor=true cherry-pick --allow-empty --allow-empty-message --keep-redundant-commits --continue`
|
161
|
-
end
|
162
|
-
rebase!(load_remaining_commits)
|
163
|
-
end
|
164
|
-
|
165
|
-
def rebase!(remaining_commits)
|
166
|
-
FileUtils.cd(repo_path) do
|
167
|
-
remaining_commits.each.with_index do |commit, commit_idx|
|
168
|
-
`git cherry-pick --allow-empty --allow-empty-message --keep-redundant-commits #{commit}`
|
169
|
-
|
170
|
-
if not $?.success?
|
171
|
-
copy_repo_to_step!
|
172
|
-
save_remaining_commits(remaining_commits[(commit_idx+1)..-1])
|
173
|
-
return false
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
save_remaining_commits(nil)
|
179
|
-
|
180
|
-
repo.references.update(repo.branches["master"], repo.head.target_id)
|
181
|
-
repo.head = "refs/heads/master"
|
182
|
-
|
183
|
-
true
|
184
|
-
end
|
185
|
-
|
186
|
-
private
|
187
|
-
|
188
|
-
def step_path
|
189
|
-
File.join(@tutorial.config[:path], "step")
|
190
|
-
end
|
191
|
-
|
192
|
-
def remaining_commits_path
|
193
|
-
File.join(@tutorial.config[:path], ".leg/remaining_commits")
|
194
|
-
end
|
195
|
-
|
196
|
-
def modified_at
|
197
|
-
if File.exist? repo_path
|
198
|
-
repo = Rugged::Repository.new(repo_path)
|
199
|
-
if master = repo.branches.find { |b| b.name == "master" }
|
200
|
-
master.target.time
|
201
|
-
end
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
def load_remaining_commits
|
206
|
-
if File.exist?(remaining_commits_path)
|
207
|
-
File.readlines(remaining_commits_path).map(&:strip).reject(&:empty?)
|
208
|
-
else
|
209
|
-
[]
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
def save_remaining_commits(remaining_commits)
|
214
|
-
if remaining_commits && !remaining_commits.empty?
|
215
|
-
File.write(remaining_commits_path, remaining_commits.join("\n"))
|
216
|
-
else
|
217
|
-
FileUtils.rm_f(remaining_commits_path)
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
def add_commit(repo, diff, message, step_num)
|
222
|
-
message ||= "~"
|
223
|
-
message.strip!
|
224
|
-
message = "~" if message.empty?
|
225
|
-
|
226
|
-
if diff
|
227
|
-
stdin = IO.popen("git apply -", "w")
|
228
|
-
stdin.write diff
|
229
|
-
stdin.close
|
230
|
-
end
|
231
|
-
|
232
|
-
index = repo.index
|
233
|
-
index.read_tree(repo.head.target.tree) unless repo.empty?
|
234
|
-
|
235
|
-
Dir["**/*"].each do |path|
|
236
|
-
unless File.directory?(path)
|
237
|
-
oid = repo.write(File.read(path), :blob)
|
238
|
-
index.add(path: path, oid: oid, mode: 0100644)
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
options = {}
|
243
|
-
options[:tree] = index.write_tree(repo)
|
244
|
-
if @tutorial.config[:repo_author_name]
|
245
|
-
options[:author] = {
|
246
|
-
name: @tutorial.config[:repo_author_name],
|
247
|
-
email: @tutorial.config[:repo_author_email],
|
248
|
-
time: Time.now
|
249
|
-
}
|
250
|
-
options[:committer] = options[:author]
|
251
|
-
end
|
252
|
-
options[:message] = message
|
253
|
-
options[:parents] = repo.empty? ? [] : [repo.head.target]
|
254
|
-
options[:update_ref] = "HEAD"
|
255
|
-
|
256
|
-
commit_oid = Rugged::Commit.create(repo, options)
|
257
|
-
|
258
|
-
if diff
|
259
|
-
repo.references.create("refs/tags/step-#{step_num}", commit_oid)
|
260
|
-
end
|
261
|
-
end
|
262
|
-
end
|