-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathgit_revision_tool
executable file
·328 lines (278 loc) · 12.7 KB
/
git_revision_tool
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
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
#!/usr/bin/perl
#
# Copyright © 2009-2014 The Regents of the University of California.
# All Rights Reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# • Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# • Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# • None of the names of any campus of the University of California, the name
# "The Regents of the University of California," or the names of any of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
# OF SUCH DAMAGE.
#
use Cwd;
use Getopt::Long;
use Pod::Usage;
use strict;
sub get_last_revision();
sub get_index_from_name($);
sub check_repository_status($);
sub create_git_tag($);
my $tag_branch = "develop";
Getopt::Long::config("bundling");
GetOptions(
"i=s" =>\my $increment_index,
"p=s" =>\my $print_index,
"b=s" =>\$tag_branch,
"o" =>\my $override_repo_check,
"v" =>\my $verbose,
"h" =>\my $help
) or pod2usage(-verbose=>2, -exitval=>2);
if( defined($help) ) {
pod2usage(-verbose=>2, -exitval=>2);
}
###############################################################################
# Get last revision
###############################################################################
my @revision = get_last_revision();
# last entry in array is the commit hash
my $last_revision_commit = pop(@revision);
my $revision_tag = join('.', @revision );
my $output_string = $revision_tag;
if (defined($increment_index) ){
$increment_index = get_index_from_name($increment_index);
###############################################################################
# Make sure there are no local changes
###############################################################################
if( !defined($override_repo_check) ) {
my $repo_status = check_repository_status($tag_branch);
if( $repo_status != 0 ) {
print "ERROR: Your local repository is not suitable for tagging.\n";
print " You must use the $tag_branch branch AND have no local changes.\n";
exit($repo_status);
}
}
my $current_commit = `git rev-parse HEAD`;
chomp($current_commit);
if( $current_commit ne $last_revision_commit ) {
# Permit the adding of one new index. For example 0.9.4 can increment to 0.9.4.1
my $max_index = $#revision + 1;
if( $increment_index > $max_index || $increment_index < 0 ) {
print "ERROR: Index ($increment_index) is out of range. Acceptable indices are 0-$max_index!\n";
exit(1);
} elsif( $increment_index == $max_index ) {
# Set the new index to 0. It will be immediately incremented to 1 below.
push(@revision, 0);
}
# increment the correct indices. All indices > the increment index are updated
for( my $i = $increment_index; $i <= $#revision; $i++ ) {
if( $i == $increment_index ) {
$revision[$i]++;
} else {
$revision[$i] = 0;
}
}
$revision_tag = join('.', @revision );
if($verbose) {
print "Creating tag: $revision_tag\n";
}
create_git_tag($revision_tag);
$output_string = $revision_tag;
} elsif ($verbose) {
print "Current commit is already tagged. Will not be incremented.\n";
}
}
if( defined($print_index) ) {
$print_index = get_index_from_name($print_index);
if( $print_index >= 0 && $print_index <= $#revision ) {
$output_string = $revision[$print_index];
} else {
print "ERROR: $print_index is outside of revision index range. Range is 0-$#revision.\n";
exit(1);
}
} else {
$output_string .= "\n";
}
print $output_string;
exit(0);
###############################################################################
#
# SUBROUTINES
#
###############################################################################
###############################################################################
# Gets the last tagged revision. Returns an array with each revision index
# and appends the commit hash to the end of the list.
###############################################################################
sub get_last_revision() {
###############################################################################
# Parse to get the current revision
###############################################################################
my @tags = `git ls-remote --tags .`;
my $last_revision_commit;
my @last_revision_number;
foreach my $tag (@tags) {
chomp($tag);
if( $tag =~/(.*)\s+refs\/tags\/(.*)/ ) {
my $revision_commit = $1;
my @revision_numbers = ($2 =~ /(\d+)/g);
for (my $index = 0; $index <= $#revision_numbers; $index++ ) {
# Assume an extra index is always later. Example 2.2.0 comes after 2.2.
if( $index > $#last_revision_number ){
@last_revision_number = @revision_numbers;
$last_revision_commit = $revision_commit;
last;
} elsif( $revision_numbers[$index] > $last_revision_number[$index] ) {
@last_revision_number = @revision_numbers;
$last_revision_commit = $revision_commit;
last;
}
}
}
}
if( !defined($last_revision_commit ) ) {
print "ERROR: Could not find any previously tagged revisions.\n";
exit(1);
}
push(@last_revision_number, $last_revision_commit);
return @last_revision_number;
}
###############################################################################
# Checks to see if there are any local changes in your git repository.
# Also ensures that the user is on the correct branch.
###############################################################################
sub check_repository_status($)
{
my( $tag_branch ) = @_;
my $return_status = 0;
# First let's check to make sure the user is on the correct branch.
# Grab the current checkout's revision
my $head_commit =`git rev-parse HEAD`;
chomp($head_commit);
# Grab the remote revision commit for the tag branch
my $tagging_commit =`git ls-remote -q -h origin $tag_branch`;
if( $tagging_commit =~/(.*)\s+refs\/heads\/$tag_branch/ ) {
$tagging_commit = $1;
} else {
print "ERROR: Could not determine current $tag_branch commit.\n";
$return_status = 1;
}
if( $head_commit ne $tagging_commit ) {
print "ERROR: Your HEAD ref <$head_commit> does not match the current $tag_branch head <$tagging_commit>.\n";
print "ERROR: Please verify you are using the $tag_branch branch and are up-to-date with the remote repo.\n";
$return_status = 1;
} else {
my $local_changes = `git diff origin/$tag_branch`;
if( $local_changes ne "" ) {
print "ERROR: You have local changes. Please commit and push.\n";
$return_status = 1;
}
}
return $return_status;
}
###############################################################################
# Creates the new git tag.
###############################################################################
sub create_git_tag($)
{
my( $new_tag ) = @_;
if( system("git tag -f $new_tag") != 0 ) {
print "ERROR: Could not create git tag: $new_tag.\n";
exit(1);
}
if( system("git push origin $new_tag") != 0 ) {
print "ERROR: Could not push git tag: $new_tag.\n";
exit(1);
}
}
###############################################################################
# Utility method to convert a named revision index to a numerical index.
###############################################################################
sub get_index_from_name($)
{
my( $requested_index ) = @_;
if(lc($requested_index) eq "major") {
$requested_index = 0;
} elsif(lc($requested_index) eq "minor") {
$requested_index = 1;
} elsif(lc($requested_index) eq "patch") {
$requested_index = 2;
}
# Throw an error if its not a number.
if ($requested_index =~ /\D/) {
print "ERROR: Not a valid index: $requested_index\n";
exit(1);
}
return $requested_index;
}
###############################################################################
#
# POD usage docs
#
###############################################################################
=head1 NAME
git_revision_tool
=head1 SYNOPSIS
git_revision_tool [-i increment_index -p print_index -b branch -n -v -h]
-i increment_index Increment the revision by the given index.
This must either be an integer or one of the
following options:
major (first index)
minor (second index)
patch (third index)
-p print_index Just print the given index of the current revision.
This must either be an integer or one of the
following options:
major (first index)
minor (second index)
patch (third index)
-b branch The branch that tags should be made on.
(default=develop)
-o Override the repository check. If not set the
script will exit with an error if the user is not
on the specified branch OR has any local changes.
-v Verbose output.
-h Print this message.
=head1 DESCRIPTION
The purpose of this script is to provide a tool for using git tags to
manage revisions in a given git project. It is assumed that the tool is
run from within a git project. There are a few different ways this script
can behave depending on the arguments used:
*If no arguments are specified then current revision number is printed.
The current revision is determined by getting all tags for the project,
parsing out only tags that are an arbitrary number of integers delimited
by '.' characters. For example 0.9.4 would be identified as a revision tag.
Only the highest revision number is printed, and it is assumed that any
additional digits in a revision number belong to a LATER revision. For
example 0.9.4.0 would be later then 0.9.4.
*If the -i flag is used then the current revision will be incremented
at the specified index. For example if '-i 0' is set the first index will be
incremented by one and all later indices are will be set to 0. The index
specified must be greater than or equal to zero and less then or equal to
the number of revision indices. If the index is set to the number of
revision indices then an extra revision index will be added. For example
if the current revision is 0.9.2 and the user specifies '-i 3' then the
new revision will be 0.9.2.1. The new index is set to one rather then
zero for clarity. This flag will trigger a check to make sure the user
is on the correct branch for tagging and that there are no local changes.
Adding the '-o' flag overrides this check.
*If the -p flag is used then only the revision number at a specific
index is printed. For example if the current version was 1.2.0 and the
user ran with '-p 1' then only '2' would be printed.
=cut