--- /dev/null
+#!/usr/bin/perl
+
+use Getopt::Long;
+use Pod::Usage;
+use File::Path;
+use File::Temp;
+
+my $tag;
+my $last;
+my $tagPoint;
+my $last;
+my $outDir = ".";
+
+GetOptions("help|?" => \$help,
+ "man" => \$man,
+ "tagpoint=s" => \$tagPoint,
+ "last=s" => \$last,
+ "dir=s" => \$outDir) or pod2usage(2);
+
+pod2usage(1) if $help;
+pod2usage(-exitstatus => 0, -verbose => 2) if $man;
+
+my $tagName = shift;
+my $version = shift;
+
+pod2usage(2) if !defined($tagName);
+
+# Tag the repository
+
+if ($tagPoint) {
+ system ("git tag -s $tagName $tagPoint") == 0
+ or die "git tag failed with : $!";
+
+ # Push the tag upstream
+ system ("git push ssh://gerrit.openafs.org:29418/openafs tag $tagName") == 0
+ or die "git push failed with : $!";
+}
+
+$version = `git describe --abbrev=4 $tagName`;
+chomp $version;
+$version=~s/openafs-[^-]*-//;
+$version=~s/_/./g;
+
+# Grab the tagged code into a temporary directory
+
+my $name = "openafs-".$version;
+
+my $tempDir = File::Temp::tempdir();
+system ("git archive --format=tar --prefix=$name/ $tagName ".
+ " | tar -C $tempDir -x") == 0
+ or die "Git archive failed with: $?";
+
+# Construct the ChangeLog
+if ($last) {
+ system("git log $last..$tagName > $outDir/ChangeLog");
+} else {
+ system("git log $tagName > $outDir/ChangeLog");
+}
+
+# Describe the tree
+system("git describe --abbrev=4 $tagName > $tempDir/$name/.version");
+
+# Run regen.sh to create the rest of the tree
+system ("cd $tempDir/$name && ./regen.sh") == 0
+ or die $!;
+
+# Create the documentation tarball
+system("tar -cf $outDir/$name-doc.tar -C $tempDir $name/doc") == 0
+ or die "Unable to create documentation tarball : $!";
+push @toCompress, "$outDir/$name-doc.tar";
+
+# Remove the docs directory (we've already build a tarball for it)
+File::Path::rmtree("$tempDir/$name/doc");
+
+# Create the source tarball (both .gz and .bz2)
+system("tar -cf $outDir/$name-src.tar -C $tempDir $name") == 0
+ or die "Unable to create documentation tarball : $!";
+push @toCompress, "$outDir/$name-src.tar";
+
+# Construct the diffs, and zip them
+if ($last) {
+ system("git diff $last..$tagName > $outDir/$name.diff") == 0
+ or die "Unable to create diff : $!";
+ push @toCompress, "$outDir/$name.diff";
+}
+
+my @toMD5 = @toCompress;
+
+# Compress everything that needs squashing
+foreach my $file (@toCompress) {
+ system("gzip < $file > $file.gz") == 0
+ or die "Unable to create gzip file of '$file' : $!";
+ push @toMD5, "$file.gz";
+
+ system("bzip2 < $file > $file.bz2") == 0
+ or die "Unable to create bzip file of '$file' : $!";
+ push @toMD5, "$file.bz2";
+}
+
+foreach my $file (@toMD5) {
+ if (-x "/sbin/md5") {
+ system("/sbin/md5 -q $file > $file.md5");
+ } elsif (-x "/usr/bin/md5sum") {
+ system("/usr/bin/md5sum $file > $file.md5");
+ } else {
+ print STDERR "No md5 utiltiy found. Not producing checksums\n";
+ }
+}
+
+
+__END__
+
+=head1 NAME
+
+make_release - Make an OpenAFS release from git
+
+=head1 SYNOPSIS
+
+make_release [options] <tag> [<version>]
+
+ Options:
+ --help brief help message
+ --man full documentation
+ --tagpoint <object> create new tag
+ --last <object> generate changelog and diffs from this point
+ --dir <dir> output results into this directory
+
+=head1 DESCRIPTION
+
+make_release constructs an OpenAFS release from a local git clone. If run
+with just the standard arguments, it will extract the contents of the
+specified tag into the current directory, creating src and doc tarballs,
+gziping and bziping them, and generating md5 hashes. It will also create a
+ChangeLog file, listing all of the changes in that release.
+
+This standard behaviour may be modified by the following options
+
+=head1 OPTIONS
+
+=over 8
+
+=item B<--last> I<object>
+
+Generate the ChangeLog starting from I<object>. Also generate a
+openafs-$version.diff file in the output directory containing all of the
+changes between I<object> and the current tag
+
+=item B<--dir> I<directory>
+
+Instead of generating all of the output in the current directory, place it
+in <directory>, which must already exist.
+
+=item B<--tagpoint> I<commit|branch>
+
+Rather than using an existing tag, create a new one on the specified commit,
+or on the tip of the specified branch. This will GPG sign the new tag, and
+push it into gerrit.
+
+=cut