class GitBump

Constants

INITIAL

Public Class Methods

basename() click to toggle source
# File lib/git_bump.rb, line 182
def self.basename
  'git bump'
end
help(shell, *) click to toggle source
Calls superclass method
# File lib/git_bump.rb, line 267
  def self.help(shell, *)
    super
    shell.say <<-EOS
With no arguments, git bump defaults to creating a release with the least
significant component of the version number incremented.  For example,
1.2.3-rc4 becomes 1.2.3-rc5, while 6.7 becomes 6.8.  To override, provide a
version number argument, or one of the following keywords:

major: bump the most significant component
minor: bump the second most significant component
point: bump the third most significant component
    EOS
  end
start() click to toggle source
Calls superclass method
# File lib/git_bump.rb, line 17
def self.start
  ARGV.unshift('release') if ARGV.first =~ /^v?\d/ || %w(major minor point).include?(ARGV.first)
  super
end

Public Instance Methods

generate_version(request) click to toggle source
# File lib/git_bump.rb, line 111
def generate_version(request)
  if request =~ /^v?(\d.*)/
    $1
  elsif latest
    components = latest.version.to_s.split('.')
    case request
    when 'major' then increment(0, components)
    when 'minor' then increment(1, components)
    when 'point' then increment(2, components)
    when nil     then components.last.succ!
    else
      abort "Unrecognized version increment #{request}."
    end
    components.join('.')
  else
    abort "Appears to be initial release.  Version number required."
  end
end
increment(pos, components) click to toggle source
# File lib/git_bump.rb, line 99
def increment(pos, components)
  components[pos].sub!(/^(\d+).*/, '\1')
  components[pos].succ!
  (components.size-1).downto(pos+1) do |i|
    if components[i] =~ /^\d/
      components[i] = '0'
    else
      components.delete_at(i)
    end
  end
end
latest() click to toggle source
# File lib/git_bump.rb, line 93
def latest
  @latest ||= releases.reverse.detect do |release|
    %x{git merge-base #{release.sha1} HEAD}.chomp == release.sha1
  end
end
log(*args) click to toggle source
# File lib/git_bump.rb, line 237
def log(*args)
  if latest
    exec('git', 'log', "#{latest.sha1}..", *args)
  else
    exec('git', 'log', *args)
  end
end
logs() click to toggle source
# File lib/git_bump.rb, line 152
def logs
  if (releases.size < 2 || latest.format) && !@logs
    @logs = %x{git log --no-merges --reverse --pretty=format:"#{latest.format || '* %s.'}" #{latest.sha1}..}
    abort unless $?.success?
  end
  @logs
end
name() click to toggle source
# File lib/git_bump.rb, line 130
def name
  if latest
    latest.name
  else
    File.basename(Dir.getwd)
  end
end
next(specifier = nil) click to toggle source
# File lib/git_bump.rb, line 263
def next(specifier = nil)
  puts generate_version(specifier)
end
patch(version, force = false) click to toggle source
# File lib/git_bump.rb, line 138
def patch(version, force = false)
  diff = latest.inverse_diff(force ? 0 : 1)
  return unless diff
  deletion = /^-(.*)(#{Regexp.escape(latest.version.to_s)})(.*)\n/
  patch = diff.gsub(/#{deletion}\+\1(.*)\3\n/) do
    "-#$1#$2#$3\n+#$1#{version}#$3\n"
  end.gsub(/^(@@ -\d+,\d+ \+\d+,)(\d+) @@\n( .*\n)?#{deletion}(?![+-])/) do
    "#$1#{$2.succ} @@\n#$3-#$4#$5#$6\n+#$4#{version}#$6\n "
  end.scan(/^[d@].*\n(?:[^d@].*\n)+/).reject do |v|
    v[0] == ?@ && !v.include?(version)
  end.join.gsub(/^diff.*\n([^+-].*\n)*---.*\n\+\+\+.*\n(\Z|diff)/, '\1')
  patch unless patch =~ /\Aindex.*\Z/
end
redo() click to toggle source
# File lib/git_bump.rb, line 225
def redo
  unless %x{git diff}.empty?
    abort "Discard or stage your changes."
  end
  unless latest && latest.sha1 == %x{git rev-parse HEAD}.chomp
    abort "Can only amend the top-most commit."
  end
  system!('git', 'commit', '--amend', '--verbose', '--reset-author')
  tag!(latest.tag)
end
release(request=nil) click to toggle source
# File lib/git_bump.rb, line 189
def release(request=nil)
  version = generate_version(request)
  unless %x{git rev-parse --verify -q v#{version}}.empty? || options[:force]
    abort "Tag already exists.  If it hasn't been pushed yet, use --force to override."
  end
  initial_commit = %x{git rev-parse --verify -q HEAD}.empty?
  if !initial_commit && %x{git diff HEAD}.empty?
    abort INITIAL unless latest
    failure = "Couldn't patch.  Update the version number in the work tree and try again."
    abort failure unless patch = patch(version, options[:force])
    IO.popen(['git', 'apply', '--unidiff-zero', '--index'], 'w') do |o|
      o.write patch
    end
    abort failure unless $?.success?
    hard = true
  elsif %x{git diff --cached}.empty?
    # TODO: what happens on initial with some unstaged changes?
    abort "Discard or stage your changes."
  end
  require 'tempfile'
  Tempfile.open('git-commit') do |f|
    f.puts [name, version].join(' ')
    f.puts
    f.write logs if latest
    f.flush
    system('git', 'commit', '--file', f.path, '--edit', * initial_commit ? [] : ['--verbose'])
    unless $?.success?
      system('git', 'reset', '-q', '--hard', 'HEAD') if hard
      abort
    end
  end
  tag!("v#{version}")
end
releases() click to toggle source
# File lib/git_bump.rb, line 82
def releases
  @releases ||=
    begin
      out = %x{git for-each-ref "refs/tags/v[0-9]*" --sort="*committerdate" --format="%(refname:short) %(*objectname) %(subject)"}
      exit 1 unless $?.success?
      out.scan(/^(\S+) (\w+) (.*) (\d\S*)\s*$/).map do |args|
        Release.new(*args)
      end
    end
end
show(version = latest ? latest.version.to_s : nil) click to toggle source
# File lib/git_bump.rb, line 247
def show(version = latest ? latest.version.to_s : nil)
  release = releases.detect do |r|
    r.version.to_s == version || r.tag == version
  end
  if release
    if options[:version_only]
      puts release.version
    else
      exec('git', 'log', '-1', '--pretty=format:%B', release.sha1)
    end
  else
    exit 1
  end
end
system!(*args) click to toggle source
# File lib/git_bump.rb, line 176
def system!(*args)
  system(*args)
  abort "Error running Git." unless $?.success?
end
tag!(name) click to toggle source
# File lib/git_bump.rb, line 160
    def tag!(name)
      annote = if latest && !latest.tag_signed? then '-a' else '-s' end
      format = if releases.size < 2 || latest.tag_body? then '%B' else '%s' end
      body = %x{git log -1 --pretty=format:#{format}}
      if system('git', 'tag', '-f', annote, name, '-m', body)
        puts <<-EOS
Successfully created #{name}.  If you made a mistake, use `git bump redo` to
try again.  Once you are satisfied with the result, run

        git push origin master #{name}
        EOS
      else
        abort "Tag failed.  Create it by hand or use git reset --soft HEAD^ to try again."
      end
    end