Class: Leg::Representations::Git

Inherits:
BaseRepresentation show all
Defined in:
lib/leg/representations/git.rb

Defined Under Namespace

Classes: State

Instance Method Summary collapse

Methods inherited from BaseRepresentation

#exists?, #initialize, #modified?

Constructor Details

This class inherits a constructor from Leg::Representations::BaseRepresentation

Instance Method Details

#checkout!(step_number) ⇒ Object



153
154
155
156
157
158
159
160
161
162
# File 'lib/leg/representations/git.rb', line 153

def checkout!(step_number)
  each_step do |cur_step, commit|
    if cur_step == step_number
      FileUtils.cd(repo_path) { `git checkout #{commit.oid} 2>/dev/null` }
      save_state(load_state.step!(step_number))
      copy_repo_to_step!
      return true
    end
  end
end

#commit!(options = {}) ⇒ Object



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/leg/representations/git.rb', line 164

def commit!(options = {})
  copy_step_to_repo!
  remaining_commits = repo.branches["master"] ? commits(after: repo.head.target.oid).map(&:oid) : []
  FileUtils.cd(repo_path) do
    `git add -A`

    cmd = ["git", "commit", "-q"]
    cmd << "--amend" if options[:amend]
    cmd << "-m" << options[:message] if options[:message]
    cmd << "--no-edit" if options[:use_default_message] && options[:amend]
    cmd << "-m" << "Untitled step" if options[:use_default_message] && !options[:amend]
    system(*cmd)
  end
  if options[:amend]
    save_state(load_state.amend!)
  else
    save_state(load_state.add_commit!)
  end
  if options[:no_rebase]
    save_remaining_commits(remaining_commits)
    true
  else
    rebase!(remaining_commits)
  end
end

#copy_repo_to_step!Object



70
71
72
73
74
75
76
77
78
79
# File 'lib/leg/representations/git.rb', line 70

def copy_repo_to_step!
  FileUtils.mkdir_p(step_path)
  FileUtils.rm_rf(File.join(step_path, "."), secure: true)
  FileUtils.cd(repo_path) do
    files = Dir.glob("*", File::FNM_DOTMATCH) - [".", "..", ".git"]
    files.each do |f|
      FileUtils.cp_r(f, File.join(step_path, f))
    end
  end
end

#copy_step_to_repo!Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/leg/representations/git.rb', line 81

def copy_step_to_repo!
  FileUtils.mv(
    File.join(repo_path, ".git"),
    File.join(repo_path, "../.gittemp")
  )
  FileUtils.rm_rf(File.join(repo_path, "."), secure: true)
  FileUtils.mv(
    File.join(repo_path, "../.gittemp"),
    File.join(repo_path, ".git")
  )
  FileUtils.cd(step_path) do
    files = Dir.glob("*", File::FNM_DOTMATCH) - [".", ".."]
    files.each do |f|
      FileUtils.cp_r(f, File.join(repo_path, f))
    end
  end
end

#each_commit(options = {}) ⇒ Object Also known as: commits



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/leg/representations/git.rb', line 107

def each_commit(options = {})
  walker = Rugged::Walker.new(repo)
  walker.sorting(Rugged::SORT_TOPO | Rugged::SORT_REVERSE)

  master_commit = repo.branches["master"].target
  walker.push(master_commit)

  return [] if master_commit.oid == options[:after]
  walker.hide(options[:after]) if options[:after]

  return walker.to_a if not block_given?

  walker.each do |commit|
    yield commit
  end
end

#each_step(git_diff_options = {}) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/leg/representations/git.rb', line 126

def each_step(git_diff_options = {})
  empty_tree = Rugged::Tree.empty(repo)
  step_num = 1
  each_commit do |commit|
    commit_message = commit.message.strip
    summary = commit_message.lines.first.strip
    text = (commit_message.lines[2..-1] || []).join.strip
    next if commit_message == "-"
    commit_message = "" if commit_message == "~"
    last_commit = commit.parents.first
    diff = (last_commit || empty_tree).diff(commit, git_diff_options)
    patches = diff.each_patch.to_a

    if patches.empty?
      yield nil, commit, summary, text, patches
    else
      yield step_num, commit, summary, text, patches
      step_num += 1
    end
  end
end

#init!Object



148
149
150
151
# File 'lib/leg/representations/git.rb', line 148

def init!
  FileUtils.mkdir_p(repo_path)
  FileUtils.cd(repo_path) { `git init` }
end

#load!(options = {}) ⇒ Object

Options:

full_diffs: If true, diffs contain the entire file in one hunk instead of
  multiple contextual hunks.
diffs_ignore_whitespace: If true, diffs don't show changes to lines when
  only the amount of whitespace is changed.


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
# File 'lib/leg/representations/git.rb', line 39

def load!(options = {})
  git_diff_options = {}
  git_diff_options[:context_lines] = 100_000 if options[:full_diffs]
  git_diff_options[:ignore_whitespace_change] = true if options[:diffs_ignore_whitespace]

  page = nil
  tutorial = Leg::Tutorial.new(@config)
  each_step(git_diff_options) do |step_num, commit, summary, text, patches|
    if patches.empty?
      if summary =~ /^~~~ (.+)$/
        tutorial << page unless page.nil?

        page = Leg::Page.new($1)
        page.footer_text = text unless text.empty?
      else
        puts "Warning: ignoring empty commit."
      end
    else
      patch = patches.map(&:to_s).join("\n")
      step_diffs = Leg::Diff.parse(patch)

      page ||= Leg::Page.new
      page << Leg::Step.new(step_num, summary, text, step_diffs)

      yield step_num if block_given?
    end
  end
  tutorial << page unless page.nil?
  tutorial
end

#rebase!(remaining_commits) ⇒ Object



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/leg/representations/git.rb', line 203

def rebase!(remaining_commits)
  FileUtils.cd(repo_path) do
    remaining_commits.each.with_index do |commit, commit_idx|
      `git cherry-pick --allow-empty --allow-empty-message --keep-redundant-commits #{commit} 2>/dev/null`

      if not $?.success?
        copy_repo_to_step!
        save_remaining_commits(remaining_commits[(commit_idx+1)..-1])
        save_state(load_state.conflict!)
        return false
      end
    end
  end

  save_remaining_commits(nil)
  save_state(nil)

  repo.references.update(repo.branches["master"], repo.head.target_id)
  repo.head = "refs/heads/master"

  copy_repo_to_step!

  true
end

#rebase_remaining!Object



199
200
201
# File 'lib/leg/representations/git.rb', line 199

def rebase_remaining!
  rebase!(load_remaining_commits)
end

#repoObject



103
104
105
# File 'lib/leg/representations/git.rb', line 103

def repo
  @repo ||= Rugged::Repository.new(repo_path)
end

#repo_pathObject



99
100
101
# File 'lib/leg/representations/git.rb', line 99

def repo_path
  File.join(@config.path, ".leg/repo")
end

#reset!Object



228
229
230
231
232
233
234
235
236
237
# File 'lib/leg/representations/git.rb', line 228

def reset!
  save_state(nil)
  save_remaining_commits(nil)
  FileUtils.cd(repo_path) do
    `git cherry-pick --abort 2>/dev/null`
  end
  repo.head = "refs/heads/master"
  repo.checkout_head(strategy: :force)
  copy_repo_to_step!
end

#resolve!Object



190
191
192
193
194
195
196
197
# File 'lib/leg/representations/git.rb', line 190

def resolve!
  copy_step_to_repo!
  FileUtils.cd(repo_path) do
    `git add -A`
    `git -c core.editor=true cherry-pick --allow-empty --allow-empty-message --keep-redundant-commits --continue 2>/dev/null`
  end
  rebase!(load_remaining_commits)
end

#save!(tutorial, options = {}) ⇒ Object



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
# File 'lib/leg/representations/git.rb', line 4

def save!(tutorial, options = {})
  FileUtils.rm_rf(repo_path)
  FileUtils.mkdir_p(repo_path)

  FileUtils.cd(repo_path) do
    repo = Rugged::Repository.init_at(".")

    step_num = 1
    tutorial.pages.each do |page|
      message = "~~~ #{page.filename}"
      message << "\n\n#{page.footer_text}" if page.footer_text
      add_commit(repo, nil, message, step_num)
      page.steps.each do |step|
        message = "#{step.summary}\n\n#{step.text}".strip
        add_commit(repo, step.to_patch, message, step_num)

        yield step_num if block_given?
        step_num += 1
      end
    end

    #if options[:extra_path]
    #  FileUtils.cp_r(File.join(options[:extra_path], "."), ".")
    #  add_commit(repo, nil, "-", step_num, counter)
    #end

    repo.checkout_head(strategy: :force) if repo.branches["master"]
  end
end

#stateObject



239
240
241
# File 'lib/leg/representations/git.rb', line 239

def state
  load_state
end