git-bump 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/git-bump +7 -0
- data/lib/git_bump.rb +257 -0
- metadata +90 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b5a25a12af0749f43938f0f73f72058606826ccb
|
4
|
+
data.tar.gz: 3e33b5cc7c489af869f1f9948658fd8678252cc3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5d6c775156eb71ab824bd7a2185359cfaf4d84555a921fa7d1e21b1182b5a281bea751e465925d3690573a854bf45222de30ca6f22db1e7018cdeb5d9343e906
|
7
|
+
data.tar.gz: 0e1968dd66dfab8357336e102977e3d3f697d4915f3bb38d207098b0c684e9135d254ad6054cbd42b1d6156540c2b648c599ba53f1c6c0a44b07d293c8868b47
|
data/bin/git-bump
ADDED
data/lib/git_bump.rb
ADDED
@@ -0,0 +1,257 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
|
5
|
+
class GitBump < Thor
|
6
|
+
|
7
|
+
INITIAL = <<-EOS
|
8
|
+
Looks like this is your first release. Please add the version number to the
|
9
|
+
work tree (e.g., in your Makefile), stage your changes, and run git bump again.
|
10
|
+
|
11
|
+
If this isn't your first release, tag your most recent prior release so that
|
12
|
+
git bump can find it:
|
13
|
+
|
14
|
+
git tag -s v1.2.3 8675309
|
15
|
+
EOS
|
16
|
+
|
17
|
+
def self.start
|
18
|
+
ARGV.unshift('release') if ARGV.first =~ /^v?\d/ || %w(major minor point).include?(ARGV.first)
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
class Release
|
23
|
+
attr_reader :tag, :sha1, :name, :version
|
24
|
+
|
25
|
+
def initialize(tag, sha1, name, version)
|
26
|
+
@tag, @sha1, @name, @version = tag, sha1, name, Version.new(version)
|
27
|
+
end
|
28
|
+
|
29
|
+
def body
|
30
|
+
@body ||= %x{git log -1 --pretty=format:%b #{sha1}}
|
31
|
+
end
|
32
|
+
|
33
|
+
def format
|
34
|
+
body[/(?:\n |.)*/].sub(/\A([-* ]*)(.*?)(\.?)\z/m, '\1%s\3') unless body.empty?
|
35
|
+
end
|
36
|
+
|
37
|
+
def inverse_diff(context = 1)
|
38
|
+
unless defined?(@inverse_diff)
|
39
|
+
@inverse_diff =
|
40
|
+
if !%x{git rev-parse --verify -q #{sha1}^}.empty?
|
41
|
+
%x{git diff -U#{context} #{sha1}..#{sha1}^}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
@inverse_diff
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
class Version
|
50
|
+
def initialize(string)
|
51
|
+
@components = string.split('.')
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_s
|
55
|
+
@components.join('.')
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_a
|
59
|
+
@components.dup
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
no_tasks do
|
64
|
+
def releases
|
65
|
+
@releases ||=
|
66
|
+
begin
|
67
|
+
out = %x{git for-each-ref "refs/tags/v[0-9]*" --sort="*committerdate" --format="%(refname:short) %(*objectname) %(subject)"}
|
68
|
+
exit 1 unless $?.success?
|
69
|
+
out.scan(/^(\S+) (\w+) (.*) (\d\S*)\s*$/).map do |args|
|
70
|
+
Release.new(*args)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def latest
|
76
|
+
@latest ||= releases.reverse.detect do |release|
|
77
|
+
%x{git merge-base #{release.sha1} HEAD}.chomp == release.sha1
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def increment(pos, components)
|
82
|
+
components[pos].sub!(/^(\d+).*/, '\1')
|
83
|
+
components[pos].succ!
|
84
|
+
(components.size-1).downto(pos+1) do |i|
|
85
|
+
if components[i] =~ /^\d/
|
86
|
+
components[i] = '0'
|
87
|
+
else
|
88
|
+
components.delete_at(i)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def generate_version(request)
|
94
|
+
if request =~ /^v?(\d.*)/
|
95
|
+
$1
|
96
|
+
elsif latest
|
97
|
+
components = latest.version.to_s.split('.')
|
98
|
+
case request
|
99
|
+
when 'major' then increment(0, components)
|
100
|
+
when 'minor' then increment(1, components)
|
101
|
+
when 'point' then increment(2, components)
|
102
|
+
when nil then components.last.succ!
|
103
|
+
else
|
104
|
+
abort "Unrecognized version increment #{request}."
|
105
|
+
end
|
106
|
+
components.join('.')
|
107
|
+
else
|
108
|
+
abort "Appears to be initial release. Version number required."
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def name
|
113
|
+
if latest
|
114
|
+
latest.name
|
115
|
+
else
|
116
|
+
File.basename(Dir.getwd)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def patch(version, force = false)
|
121
|
+
diff = latest.inverse_diff(force ? 0 : 1)
|
122
|
+
return unless diff
|
123
|
+
deletion = /^-(.*)(#{Regexp.escape(latest.version.to_s)})(.*)\n/
|
124
|
+
patch = diff.gsub(/#{deletion}\+\1(.*)\3\n/) do
|
125
|
+
"-#$1#$2#$3\n+#$1#{version}#$3\n"
|
126
|
+
end.gsub(/^(@@ -\d+,\d+ \+\d+,)(\d+) @@\n( .*\n)?#{deletion}(?![+-])/) do
|
127
|
+
"#$1#{$2.succ} @@\n#$3-#$4#$5#$6\n+#$4#{version}#$6\n "
|
128
|
+
end.scan(/^[d@].*\n(?:[^d@].*\n)+/).reject do |v|
|
129
|
+
v[0] == ?@ && !v.include?(version)
|
130
|
+
end.join.gsub(/^diff.*\n([^+-].*\n)*---.*\n\+\+\+.*\n(\Z|diff)/, '\1')
|
131
|
+
patch unless patch =~ /\Aindex.*\Z/
|
132
|
+
end
|
133
|
+
|
134
|
+
def logs
|
135
|
+
if (releases.size < 2 || latest.format) && !@logs
|
136
|
+
@logs = %x{git log --no-merges --reverse --pretty=format:"#{latest.format || '* %s.'}" #{latest.sha1}..}
|
137
|
+
abort unless $?.success?
|
138
|
+
end
|
139
|
+
@logs
|
140
|
+
end
|
141
|
+
|
142
|
+
def tag!(name)
|
143
|
+
subject = %x{git log -1 --pretty=format:%s}.chomp
|
144
|
+
system!('git', 'tag', '-f', '-s', name, '-m', subject)
|
145
|
+
puts <<-EOS
|
146
|
+
Successfully created #{name}. If you made a mistake, use `git bump redo` to
|
147
|
+
try again. Once you are satisfied with the result, run
|
148
|
+
|
149
|
+
git push origin master #{name}
|
150
|
+
EOS
|
151
|
+
end
|
152
|
+
|
153
|
+
def system!(*args)
|
154
|
+
system(*args)
|
155
|
+
abort "Error running Git." unless $?.success?
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.basename
|
160
|
+
'git bump'
|
161
|
+
end
|
162
|
+
|
163
|
+
default_task 'release'
|
164
|
+
desc '[version]', 'Create and tag a release for the given version'
|
165
|
+
method_options %w(force -f) => :boolean
|
166
|
+
def release(request=nil)
|
167
|
+
version = generate_version(request)
|
168
|
+
unless %x{git rev-parse --verify -q v#{version}}.empty? || options[:force]
|
169
|
+
abort "Tag already exists. If it hasn't been pushed yet, use --force to override."
|
170
|
+
end
|
171
|
+
initial_commit = %x{git rev-parse --verify -q HEAD}.empty?
|
172
|
+
if !initial_commit && %x{git diff HEAD}.empty?
|
173
|
+
abort INITIAL unless latest
|
174
|
+
failure = "Couldn't patch. Update the version number in the work tree and try again."
|
175
|
+
abort failure unless patch = patch(version, options[:force])
|
176
|
+
IO.popen(['git', 'apply', '--unidiff-zero', '--index'], 'w') do |o|
|
177
|
+
o.write patch
|
178
|
+
end
|
179
|
+
abort failure unless $?.success?
|
180
|
+
hard = true
|
181
|
+
elsif %x{git diff --cached}.empty?
|
182
|
+
# TODO: what happens on initial with some unstaged changes?
|
183
|
+
abort "Discard or stage your changes."
|
184
|
+
end
|
185
|
+
require 'tempfile'
|
186
|
+
Tempfile.open('git-commit') do |f|
|
187
|
+
f.puts [name, version].join(' ')
|
188
|
+
f.puts
|
189
|
+
f.write logs if latest
|
190
|
+
f.flush
|
191
|
+
system('git', 'commit', '--file', f.path, '--edit', * initial_commit ? [] : ['--verbose'])
|
192
|
+
unless $?.success?
|
193
|
+
system('git', 'reset', '-q', '--hard', 'HEAD') if hard
|
194
|
+
abort
|
195
|
+
end
|
196
|
+
end
|
197
|
+
tag!("v#{version}")
|
198
|
+
end
|
199
|
+
|
200
|
+
desc 'redo', 'amend the previous release and retag'
|
201
|
+
method_options %w(force -f) => :boolean
|
202
|
+
def redo
|
203
|
+
unless %x{git diff}.empty?
|
204
|
+
abort "Discard or stage your changes."
|
205
|
+
end
|
206
|
+
unless latest.sha1 == %x{git rev-parse HEAD}.chomp
|
207
|
+
abort "Can only amend the top-most commit."
|
208
|
+
end
|
209
|
+
system!('git', 'commit', '--amend', '--verbose', '--reset-author')
|
210
|
+
tag!(latest.tag)
|
211
|
+
end
|
212
|
+
|
213
|
+
desc 'log', 'Show the git log since the last release'
|
214
|
+
def log(*args)
|
215
|
+
if latest
|
216
|
+
exec('git', 'log', "#{latest.sha1}..", *args)
|
217
|
+
else
|
218
|
+
exec('git', 'log', *args)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
desc 'show [version]', 'Show the most recent or given release'
|
223
|
+
method_options :version_only => :boolean
|
224
|
+
def show(version = latest ? latest.version.to_s : nil)
|
225
|
+
release = releases.detect do |r|
|
226
|
+
r.version.to_s == version || r.tag == version
|
227
|
+
end
|
228
|
+
if release
|
229
|
+
if options[:version_only]
|
230
|
+
puts release.version
|
231
|
+
else
|
232
|
+
exec('git', 'log', '-1', '--pretty=format:%B', release.sha1)
|
233
|
+
end
|
234
|
+
else
|
235
|
+
exit 1
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
desc 'next', 'Show the version number that would be released'
|
240
|
+
def next(specifier = nil)
|
241
|
+
puts generate_version(specifier)
|
242
|
+
end
|
243
|
+
|
244
|
+
def self.help(shell, *)
|
245
|
+
super
|
246
|
+
shell.say <<-EOS
|
247
|
+
With no arguments, git bump defaults to creating a release with the least
|
248
|
+
significant component of the version number incremented. For example,
|
249
|
+
1.2.3-rc4 becomes 1.2.3-rc5, while 6.7 becomes 6.8. To override, provide a
|
250
|
+
version number argument, or one of the following keywords:
|
251
|
+
|
252
|
+
major: bump the most significant component
|
253
|
+
minor: bump the second most significant component
|
254
|
+
point: bump the third most significant component
|
255
|
+
EOS
|
256
|
+
end
|
257
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: git-bump
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tim Pope
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-04-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: thor
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Git based release management
|
56
|
+
email:
|
57
|
+
- code@tpope.net
|
58
|
+
executables:
|
59
|
+
- git-bump
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- bin/git-bump
|
64
|
+
- lib/git_bump.rb
|
65
|
+
homepage: https://tpo.pe/git-bump
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
metadata: {}
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 2.0.3
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: Create Git release commits and tags with changelogs
|
89
|
+
test_files: []
|
90
|
+
has_rdoc:
|