Permalink
Fetching contributors…
Cannot retrieve contributors at this time
187 lines (161 sloc) 6.59 KB
require 'time'
module StepUp
module Driver
class Git < Base
VERSION_MESSAGE_FILE_PATH = ".git/TAG_EDITMSG"
NOTE_MESSAGE_FILE_PATH = ".git/NOTE_EDITMSG"
include GitExtensions::Notes
def unsupported_scm_banner
if `git --version`.chomp =~ /\d\.[^\s]+/
v = $&
if Gem::Version.correct?(v)
if Gem::Version.new("1.7.1") > Gem::Version.new(v)
"Unsupported installed GIT version: #{v}\n" +
"Please install version 1.7.1 or newer"
end
else
"Installed GIT version unknown: #{v}"
end
else
super
end
end
def self.last_version
new.last_version_tag
end
def empty_repository?
`git branch`.empty?
end
def commit_history(commit_base, *args)
return [] if empty_repository?
options = args.last.is_a?(Hash) ? args.pop : {}
top = args.shift
top = "-n#{ top }" unless top.nil?
encoding = CONFIG["log_output_encoding"] || "ISO-8859-1"
commits = `git log --pretty=oneline --encoding=#{ encoding } --no-color #{ top } #{ commit_base }`
commits.force_encoding(encoding) if RUBY_VERSION.to_f >= 1.9
begin
if options[:with_messages]
commits.scan(/^(\w+)\s+(.*)$/)
else
commits.scan(/^(\w+)\s/).flatten
end
rescue ArgumentError => e
if e.message.start_with?("invalid byte sequence")
puts <<-MSG
Could not read information from git-log because of an #{e.message}.
To fix this you can insert an attribute "log_output_encoding" on
file .stepuprc into your GIT project. Example:
\033[30;1m# .stepuprc\033[0m
\033[32m+log_output_encoding: "ISO-8859-1"\033[0m
notes:
after_versioned:
strategy: "keep" # Valid options: "keep" or "remove"
\033[30;1m# ...\033[0m
To see the current encoding from your GIT repository, you may
check with the following bash command:
`git log --pretty=oneline --no-color > test.txt && file test.txt && rm test.txt`
MSG
exit 1
end
raise
end
end
def commits_between(first_commit, last_commit = "HEAD", *args)
commit_base = first_commit.nil? ? last_commit : "#{ first_commit }..#{ last_commit }"
commit_history(commit_base, *args)
end
def tags
@tags ||= `git tag -l`
end
def objects_with_notes_of(ref)
`git notes --ref=#{ ref } list`.scan(/\w+$/)
end
def note_message(ref, commit)
`git notes --ref=#{ ref } show #{ commit }`
end
def all_version_tags
@version_tags ||= tags.scan(mask.regex).map{ |tag| tag.collect(&:to_i) }.sort.map{ |tag| mask.format(tag) }.reverse
end
def version_tag_info(tag)
full_message = `git show #{ tag } --pretty=medium --no-decorate --quiet --date=default`.force_encoding('utf-8')
tag_pattern = tag.gsub(/\./, '\\.')
tag_message = full_message[/^tag\s#{tag_pattern}.*?\n\n(.*?)\n\n(?:tag\s[^\s]+|commit\s\w{40})\n/m, 1] || ""
tagger = full_message[/\A.*?\nTagger:\s(.*?)\s</m, 1]
date = Time.parse(full_message[/\A.*?\nDate:\s+(.*?)\n/m, 1])
{:message => tag_message, :tagger => tagger, :date => date}
end
def detached_notes_as_hash(commit_base = "HEAD", notes_sections = nil)
tag = all_version_tags.any? ? cached_last_version_tag(commit_base) : nil
tag = tag.sub(/\+$/, '') unless tag.nil?
RangedNotes.new(self, tag, commit_base, :notes_sections => notes_sections).notes.as_hash
end
def next_release_level(commit_base)
level = CONFIG.versioning.version_levels.last
if CONFIG.versioning["auto_increment"].is_a?(Hash)
detached_notes = cached_detached_notes_as_hash(commit_base)
CONFIG.versioning.version_levels.reverse.each do |name|
sections = CONFIG.versioning.auto_increment.sections_level[name]
next if sections.nil?
level = name if detached_notes.any?{ |section, notes| sections.include?(section) && notes.any? }
end
end
level
end
def next_version_tag(commit_base, level = nil)
level = next_release_level(commit_base) if level.nil?
if commit_base && mask.parse(commit_base)
mask.increase_version(commit_base, level)
else
tag = cached_last_version_tag(commit_base)
tag =~ /\+/ ? mask.increase_version($`, level) : nil
end
end
def steps_to_increase_version(level, commit_base = "HEAD", message = nil)
new_tag = next_version_tag(commit_base, level)
if new_tag.nil? or new_tag.empty?
STDERR.puts "Current release #{cached_last_version_tag(commit_base)}"
exit 0
end
notes = cached_detached_notes_as_hash(commit_base)
commands = []
commands << "git fetch" if cached_fetched_remotes.any?
commands << "git tag -a -m \"#{ (message || notes.to_changelog).gsub(/([\$\\"`])/, '\\\\\1') }\" #{ new_tag } #{ commit_base }"
commands << "git push #{cached_fetched_remotes("notes").first} refs/tags/#{new_tag}" if cached_fetched_remotes.any?
commands + steps_for_archiving_notes(notes, new_tag)
end
def last_version_tag(commit_base = "HEAD", count_commits = false)
all_versions = all_version_tags
unless all_versions.empty?
commits = cached_commit_history(commit_base)
all_versions.each do |tag|
commit_under_the_tag = commit_history(tag, 1).first
index = commits.index(commit_under_the_tag)
unless index.nil?
unless index.zero?
count = count_commits == true ? commits_between(tag, commit_base).size : 0
tag = "#{ tag }+#{ count unless count.zero? }"
end
return tag
end
end
count_commits == true ? zero_version(commit_base, true) : nil
else
zero_version(commit_base, count_commits)
end
end
def fetched_remotes(refs_type = 'heads')
config = `git config --get-regexp 'remote'`.split(/\n/)
config.collect{ |line|
$1 if line =~ /^remote\.(\w+)\.fetch\s\+refs\/#{ refs_type }/
}.compact.uniq.sort
end
def editor_name
ENV["GIT_EDITOR"] || ENV["EDITOR"] || `git config core.editor`.chomp
end
def zero_version(commit_base = "HEAD", count_commits = false)
"%s+%s" % [mask.blank, "#{ commit_history(commit_base).size if count_commits }"]
end
end
end
end