Class: Processor

Inherits:
Object
  • Object
show all
Defined in:
lib/ezgit/processor.rb

Constant Summary collapse

NO_BRANCH =
'(nobranch)'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(global_options) ⇒ Processor

Returns a new instance of Processor.



9
10
11
12
13
# File 'lib/ezgit/processor.rb', line 9

def initialize(global_options)
  @dry_run = global_options[:dry_run]
  @dry_run_flag = @dry_run ? '-n' : ''
  @no_prompt = global_options[:force]
end

Instance Attribute Details

#all_branchesObject (readonly)

Returns the value of attribute all_branches.



5
6
7
# File 'lib/ezgit/processor.rb', line 5

def all_branches
  @all_branches
end

#all_uniq_branchesObject (readonly)

Returns the value of attribute all_uniq_branches.



5
6
7
# File 'lib/ezgit/processor.rb', line 5

def all_uniq_branches
  @all_uniq_branches
end

#current_branchObject (readonly)

Returns the value of attribute current_branch.



5
6
7
# File 'lib/ezgit/processor.rb', line 5

def current_branch
  @current_branch
end

#dry_runObject (readonly)

Returns the value of attribute dry_run.



5
6
7
# File 'lib/ezgit/processor.rb', line 5

def dry_run
  @dry_run
end

#dry_run_flagObject (readonly)

Returns the value of attribute dry_run_flag.



5
6
7
# File 'lib/ezgit/processor.rb', line 5

def dry_run_flag
  @dry_run_flag
end

#no_promptObject (readonly)

Returns the value of attribute no_prompt.



5
6
7
# File 'lib/ezgit/processor.rb', line 5

def no_prompt
  @no_prompt
end

#remote_branch(branch_name = nil) ⇒ Object (readonly)

Returns the value of attribute remote_branch.



5
6
7
# File 'lib/ezgit/processor.rb', line 5

def remote_branch
  @remote_branch
end

Instance Method Details

#add_remote_to_branch(branch_name) ⇒ Object



34
35
36
37
38
39
40
# File 'lib/ezgit/processor.rb', line 34

def add_remote_to_branch(branch_name)
  # Get remote name.  It may not be 'origin'
  origin = `git remote show`.gsub(/\s/, '')
  remote = "#{origin}/#{branch_name}"
  remote = all_branches.include?(remote) ? remote : ''
  return remote
end

#check_local_changes(opts = nil) ⇒ Object

returns (bool has_changes?, Array changes)



165
166
167
168
169
170
# File 'lib/ezgit/processor.rb', line 165

def check_local_changes(opts = nil)
  ignored = (opts.nil? || opts[:ignored] == false) ? '' : '--ignored'
  stdin, stdout, stderr = Open3.popen3("git status --untracked-files=all --porcelain #{ignored}")
  changes = stdout.readlines
  return changes.any?, changes
end

#check_remote_statusObject

returns :up_to_date/:no_remote/:rebase/:ahead/:behind, count



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/ezgit/processor.rb', line 111

def check_remote_status
  return :headless if current_branch == NO_BRANCH
  ahead_count_rgx = /.*ahead.(\d+)/
  behind_count_rgx = /.*behind.(\d+)/
  stdin, stdout, stderr = Open3.popen3('git status -bs')
  stat = stdout.readlines[0]
  ahead_match = stat.match(ahead_count_rgx)
  ahead_count = (ahead_match.nil?) ? '0' : ahead_match[1]
  behind_match = stat.match(behind_count_rgx)
  behind_count = (behind_match.nil?) ? '0' : behind_match[1]
  case
    when ahead_count > '0' && behind_count == '0'
      return :ahead, ahead_count
    when ahead_count == '0' && behind_count > '0'
      return :behind, behind_count
    when ahead_count > '0' && behind_count > '0'
      return :rebase, '0'
    else
      return :no_remote, '0' if remote_branch.empty?
      return :up_to_date, '0'
  end
end

#clean!(wipe_ignored) ⇒ Object



200
201
202
203
204
205
206
207
208
209
210
# File 'lib/ezgit/processor.rb', line 200

def clean!(wipe_ignored)
  command = 'git clean -df'
  command += 'x' if wipe_ignored
  out, err = call_command_with_out_error(command + 'n')
  puts err.red.bold unless no_prompt
  return puts 'Nothing to clean.'.green if err.empty?
  return if dry_run
  run_lambda_with_prompt do
    puts `#{command}`
  end
end

#clone(args) ⇒ Object



191
192
193
194
195
196
197
# File 'lib/ezgit/processor.rb', line 191

def clone(args)
  return puts 'invalid number of arguments. Requires a source. (Destination is optional.)' if args.count < 1 || args.count > 2
  return if @dry_run
  puts out = `git clone #{args.first} #{args[1]}`
  repo_name = args[1] || out.split('\'')[1]
  puts 'You have created a copy of ' + args.first.to_s.bold + ' in the ' + repo_name.bold + ' directory.' if $? == 0
end

#commit(args) ⇒ Object



336
337
338
339
340
341
342
343
344
# File 'lib/ezgit/processor.rb', line 336

def commit(args)
  return puts "Please specify a message.".yellow.bold if args.count < 1
  return puts "Invalid number of arguments. Please specify only a message.".yellow.bold if args.count > 1
  has_changes, changes = check_local_changes
  return puts "There are no changes to commit".yellow.bold unless has_changes
  commit_id = args[0].to_s
  puts `git add -A`
  puts `git commit -m "#{commit_id}"`
end

#create(args) ⇒ Object



256
257
258
259
260
261
262
263
264
# File 'lib/ezgit/processor.rb', line 256

def create(args)
  return puts 'Please specify a branch name.'.yellow.bold if args.count < 1
  return puts 'Invalid number of arguments. Please specify only a branch name.'.yellow.bold if args.count > 1
  branch_name = args[0].to_s
  return puts "Would create branch: #{branch_name}" if dry_run
  `git checkout -b #{branch_name}`
  display_branch_list_with_current
  display_current_changes
end

#delete!(args) ⇒ Object



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/ezgit/processor.rb', line 267

def delete!(args)
  return puts "Please specify a branch name.".yellow.bold if args.count < 1
  return puts "Invalid number of arguments. Please specify only a branch name.".yellow.bold if args.count > 1
  branch_name = args[0].to_s
  is_master = branch_name.eql?('master') || branch_name.eql?(add_remote_to_branch('master'))
  return puts "Cannot delete ".red + branch_name.red.bold + ".".red if is_master
  branches = []
  is_local = all_branches.include?(branch_name)
  branches << branch_name if is_local
  remote_name = add_remote_to_branch(branch_name)
  is_remote = all_branches.include?(remote_name)
  branches << remote_name if is_remote
  return puts "Cannot delete ".red + branch_name.red.bold + " while you are using it. Please switch to another branch and try again.".red if branches.include?(current_branch)
  return puts "Branch does not exist: ".red + branch_name.red.bold unless branches.any?
  return puts "Would completely delete branches: #{branches.join(',')}" if @dry_run
  print "  Are you sure you want to delete '#{branch_name}'(y/n)?".red.bold unless no_prompt
  return unless run_lambda_with_prompt do
    puts `git push --delete #{remote_name.sub('/', ' ')}` if is_remote
    puts `git branch -D #{branch_name}` if is_local
    refresh_branches
    display_branch_list_with_current
  end
end

#display_branch_list_with_currentObject



87
88
89
90
91
92
93
94
95
96
97
# File 'lib/ezgit/processor.rb', line 87

def display_branch_list_with_current
  puts ''
  puts '  BRANCHES:'.bold
  brs = []
  all_uniq_branches.each do |b|
    #add an indicator if it is the current branch
    b = b.eql?(current_branch) ? "#{b} <-- CURRENT".bold : b
    # output the list
    puts "  #{b}".cyan
  end
end

#display_current_changes(opts = nil) ⇒ Object



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/ezgit/processor.rb', line 173

def display_current_changes(opts = nil)
  puts ''
  puts "  TO BE COMMITTED ON: #{current_branch}".bold
  has_changes, changes = check_local_changes(opts)
  puts "  No changes.".green unless has_changes
  changes.collect! { |line|
    line.sub!('!! ', CYAN + "  ignore  " + CLEAR)
    line.gsub!(/ U |U  /, RED + BOLD + "   MERGE  " + CLEAR)
    line.gsub!(/ D |D  /, RED + BOLD + "  Delete  " + CLEAR)
    line.gsub!(/.R |R. /, YELLOW + BOLD + "  Rename  " + CLEAR)
    line.gsub!(/A  |\?\? /, YELLOW + BOLD + "     Add  " + CLEAR)
    line.gsub!(/.M |M. /, YELLOW + BOLD + "  Change  " + CLEAR)
    line
  }
  puts changes.sort!
end

#display_log_graph(count = 5, show_all = false) ⇒ Object



76
77
78
79
80
81
82
83
84
# File 'lib/ezgit/processor.rb', line 76

def display_log_graph(count = 5, show_all = false)
  puts ''
  puts "REPOSITORY TREE".bold + "(last #{count} commits)"
  all = show_all ? '--all' : ''
  stdin, stdout, stderr = Open3.popen3("git log --graph #{all} --format=format:\"#{CYAN}%h #{CLEAR + CYAN}(%cr) #{CYAN}%cn #{CLEAR}%s#{CYAN + BOLD}%d#{CLEAR}\" --abbrev-commit --date=relative -n #{count}")
  err = stderr.readlines
  return puts 'There is no history yet.'.cyan.bold if err.any?
  puts err.join('') + stdout.readlines.join('')
end

#display_sync_statusObject



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/ezgit/processor.rb', line 135

def display_sync_status
  puts ''
  puts '  SYNC STATUS:'.bold
  stat, count = check_remote_status
  commit_s = (count == 1) ? 'commit' : 'commits'
  case stat
    when :ahead
      puts "  Your #{current_branch.bold + CYAN} branch is ahead of the remote by #{count} #{commit_s}.".cyan
      puts "  (Use 'ezgit pull' to update the remote.)".cyan
    when :behind
      puts "  Your #{current_branch.bold + YELLOW} branch is behind the remote by #{count} #{commit_s}.".yellow
      puts "  (Use 'ezgit pull' to get the new changes.)".yellow
    when :rebase
      puts "  Your #{current_branch} branch has diverged #{count} #{commit_s} from the remote.".red.bold
      puts "  (Use must use git directly to put them back in sync.)".red.bold
    when :no_remote
      puts "  Your #{current_branch.bold + CYAN} branch does not yet exist on the remote.".cyan
      puts "  (Use 'ezgit pull' to update the remote.)".cyan
    when :headless
      puts "  You are in a headless state (not on a branch)".red.bold
      puts "  (Use 'ezgit create <branch>' to create a branch at this commit,".red.bold
      puts "   or use 'ezgit switch <branch>' to switch to a branch.)".red.bold
    else
      puts "  Your #{current_branch.bold + GREEN} branch is in sync with the remote.".green
      puts "  (Use 'ezgit pull' to ensure it stays in sync.)".green
  end
end

#fetchObject



347
348
349
350
351
# File 'lib/ezgit/processor.rb', line 347

def fetch
  stdin, stdout, stderr = Open3.popen3("git fetch -p #{@dry_run_flag}")
  puts stderr.readlines.join('') + stdout.readlines.join('')
  refresh_branches
end

#goto!(args) ⇒ Object



244
245
246
247
248
249
250
251
252
253
# File 'lib/ezgit/processor.rb', line 244

def goto!(args)
  return puts "Please specify a commit id.".yellow.bold if args.count < 1
  return puts "Invalid number of arguments. Please specify only a commit id.".yellow.bold if args.count > 1
  commit_id = args[0].to_s
  return puts "Would go to #{commit_id}" if dry_run
  puts "About to go to #{commit_id}. All changes in tracked files will be lost.".red.bold unless no_prompt
  run_lambda_with_prompt do
    puts `git reset --hard #{commit_id}`
  end
end

#infoObject



100
101
102
103
104
105
106
107
# File 'lib/ezgit/processor.rb', line 100

def info
  puts '________________________________'
  display_log_graph
  display_branch_list_with_current
  display_current_changes
  display_sync_status
  puts '________________________________'
end

#prompt_for_y_nObject



213
214
215
216
217
218
219
220
221
222
223
# File 'lib/ezgit/processor.rb', line 213

def prompt_for_y_n
  begin
    system("stty raw -echo")
    input = STDIN.getc
  ensure
    system("stty -raw echo")
  end
  out = input.to_s.downcase.eql?('y')
  puts input.to_s
  return out
end

#pullObject



354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
# File 'lib/ezgit/processor.rb', line 354

def pull
  fetch
  stat, count = check_remote_status
  case stat
    when :rebase
      if @dry_run
        puts 'would merge changes'
        display_sync_status
        return
      end
      puts `git rebase #{remote_branch}`
      #TODO: CONFLICT HANDLING?
      puts 'TODO: CONFLICT HANDLING?'
    when :behind
      if @dry_run
        puts "would reset branch to #{remote_branch}"
        display_sync_status
        return
      end
      puts `git reset --hard #{remote_branch}`
    when :headless
      puts '  You cannot pull unless you are on a branch.'.red.bold
      display_sync_status
      return
  end
  info
end

#pushObject



383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
# File 'lib/ezgit/processor.rb', line 383

def push
  stat, count = check_remote_status
  if stat.eql?(:rebase) || stat.eql?(:behind)
    puts "  The remote has been updated since you began this sync.".yellow.bold
    puts "  Try running 'ezgit pull' again".yellow.bold
  elsif stat.eql?(:no_remote) || stat.eql?(:ahead)
    puts `git push -u #{remote_branch.sub('/', ' ')}`
    refresh_branches
  elsif stat.eql?(:headless)
    puts '  You cannot push unless you are on a branch.'.red.bold
  else
    #:up_to_date | :headless
  end
  info
end

#refresh_branchesObject



43
44
45
46
# File 'lib/ezgit/processor.rb', line 43

def refresh_branches
  @all_branches = nil
  @all_uniq_branches = nil
end

#reset_hard!Object



235
236
237
238
239
240
241
# File 'lib/ezgit/processor.rb', line 235

def reset_hard!
  return if @dry_run
  puts 'All changes in tracked files will be lost.'.red.bold unless no_prompt
  run_lambda_with_prompt do
    puts `git reset --hard`
  end
end

#run_lambda_with_prompt(opts = nil) ⇒ Object



226
227
228
229
230
231
232
# File 'lib/ezgit/processor.rb', line 226

def run_lambda_with_prompt(opts = nil)
  unless no_prompt
    puts 'proceed(y/n)? '.bold
    return unless prompt_for_y_n
  end
  yield
end

#switch!(mode, args) ⇒ Object

Three modes:

:switch  - switch if there are not changes. Otherwise halt!
:switch! - clobber all files before switching
:move    - move files with switch


296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/ezgit/processor.rb', line 296

def switch!(mode, args)
  return puts "Please specify a branch name.".yellow.bold if args.count < 1
  return puts "Invalid number of arguments. Please specify only a branch name.".yellow.bold if args.count > 1
  branch_name = args[0].to_s
  return puts "Please specify a valid branch." unless all_uniq_branches.include?(branch_name)
  return puts "Already on branch: #{current_branch.bold}".green if current_branch.eql?(branch_name)
  has_changes, changes = check_local_changes
  #move files with switch
  if mode == 'move'
    x = `git checkout #{branch_name}`
    return
  end
  #switch if there are not changes. Otherwise halt!
  if has_changes && mode == 'switch'
    display_current_changes
    puts "  Cannot switch branches when you have unresolved changes".red
    puts "  Use ".red + "'ezgit switch! <branch>'".red.bold + " to abandon your changes and switch anyway,".red
    puts "  or use ".red + "'ezgit move <branch>'".red.bold + " to move your changes and switch.".red
    return
  end
  #clobber all files before switching
  #respect the -f option
  if has_changes && mode == 'switch!'
    unless no_prompt
      display_current_changes
      puts ''
      print "  WARNING: You may lose changes if you switch branches without committing.".red.bold
    end
    return unless run_lambda_with_prompt do
      @no_prompt = true
      x = `git clean -df`
      x = `git checkout -f #{branch_name}`
      return
    end
    x = `git clean -df`
  end
  x = `git checkout -f #{branch_name}`
end