git-bump 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/bin/git-bump +7 -0
  3. data/lib/git_bump.rb +257 -0
  4. metadata +90 -0
@@ -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
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift(File.expand_path('../../lib', __FILE__))
4
+
5
+ require 'git_bump'
6
+
7
+ GitBump.start
@@ -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: