From: joeyh Date: Mon, 1 Oct 2007 00:20:09 +0000 (+0000) Subject: initial import X-Git-Tag: 0.1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=df5f5bd3f973501e2d64d91008544b7f2440aa2c;p=tools%2Fpristine-tar.git initial import --- diff --git a/TODO b/TODO new file mode 100644 index 0000000..a95193f --- /dev/null +++ b/TODO @@ -0,0 +1,5 @@ +* regenerate the pristine .gz too, if possible +* Would be nice to keep a md5sums list of the files in the manifest and + check and error if any are different. The error message from xdelta when + a source file doesn't have the right checksup is fairly obscure and + doesn't aid in fixing the problem. diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..83c7503 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +pristine-tar (0.1) unstable; urgency=low + + * First release. + + -- Joey Hess Sun, 30 Sep 2007 20:06:00 -0400 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +5 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..d5a5264 --- /dev/null +++ b/debian/control @@ -0,0 +1,20 @@ +Source: pristine-tar +Section: utils +Priority: optional +Build-Depends: debhelper (>= 5), dpkg-dev (>= 1.9.0) +Maintainer: Joey Hess +Standards-Version: 3.7.2 +XS-Vcs-Svn: svn://svn.debian.org/collab-maint/deb-maint/pristine-tar/trunk + +Package: pristine-tar +Architecture: all +Section: utils +Depends: xdelta +Description: regenerate pristine tarballs + pristine-tar can generate a pristine upstream tarball using only a small + binary delta file and a revision control checkout of the upstream branch. + . + The delta file is designed to be checked into revision control along-side + the upstream branch, thus allowing Debian packages to be built entirely + using sources in revision control, without the need to keep copies of + upstream tarballs. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..2a0b84f --- /dev/null +++ b/debian/copyright @@ -0,0 +1,22 @@ +pristine-tar was written by Joey Hess . + +Copyright 2007 Joey Hess + +License: + + This is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +On Debian systems, the complete text of the GPL can be found in +/usr/share/common-licenses/GPL. diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..1006ba2 --- /dev/null +++ b/debian/rules @@ -0,0 +1,30 @@ +#!/usr/bin/make -f + +build: + pod2man -c "pristine-tar" pristine-tar > pristine-tar.1 + +clean: + dh_testdir + dh_testroot + dh_clean pristine-tar.1 + +binary-arch: build + +binary-indep: build + dh_testdir + dh_testroot + dh_clean -k + dh_install pristine-tar usr/bin + dh_installdocs TODO + dh_installman *.1 + dh_installchangelogs + dh_compress + dh_fixperms + dh_perl + dh_installdeb + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary diff --git a/pristine-tar b/pristine-tar new file mode 100755 index 0000000..4d8d904 --- /dev/null +++ b/pristine-tar @@ -0,0 +1,210 @@ +#!/usr/bin/perl + +=head1 NAME + +pristine-tar - regenerate pristine tarballs + +=head1 SYNOPSIS + +B [-v] extract delta tarball +B [-v] stash tarball delta + +=head1 DESCRIPTION + +pristine-tar stash takes the specified upstream tarball, and generates a +small binary delta file that can later be used by pristine-tar extract +to recreate the tarball. + +pristine-tar extract takes the specified delta file, and the files in the +current directory, which must have identical content to those in the +upstream tarball, and uses these to regenerate the pristine upstream +tarball. + +This is useful when maintaining a Debian package in revision control, as +you can check the delta file into revision control, tag the upstream +release in revision control, and later extract the upstream tarball from +revison control. + +pristine-tar supports compressed tarballs, but only the actual tar archive +will be identical. It does not try to preserve a bit-identical zgzip file. + +=head1 OPTIONS + +=over 4 + +=item -v + +Verbose mode, show each command that is run. + +=head1 AUTHOR + +Joey Hess + +Licensed under the GPL, version 2 or above. + +=cut + +use warnings; +use strict; +use File::Temp qw(tempdir); +use File::Path; +use Getopt::Long; + +my $verbose=0; + +sub usage { + print STDERR "Usage: pristine-tar [-v] extract delta tarball\n"; + print STDERR " pristine-tar [-v] stash tarball delta\n"; +} + +sub doit { + print "@_\n" if $verbose; + if (system(@_) != 0) { + die "command failed: @_\n"; + } +} + +sub gentarball { + my $tempdir=shift; + my $source=shift; + my $clobber_source=shift; + + doit("mkdir $tempdir/workdir"); + + my @manifest; + open (IN, "$tempdir/manifest") || die "$tempdir/manifest: $!"; + while () { + chomp; + push @manifest, $_; + } + close IN; + + # The manifest and source should have the same filenames, + # but the manifest probably has all the files under a common + # subdirectory. Check if it does. + # XXX currently only zero or one subdirectory level is supported + my $subdir=""; + foreach my $file (@manifest) { + if ($file=~m!^(/?[^/]+)/!) { + if (length $subdir && $subdir ne $1) { + last; + } + elsif (! length $subdir) { + $subdir=$1; + } + } + } + + if (! $clobber_source) { + doit("cp -a $source $tempdir/workdir/$subdir"); + } + else { + doit("mv $source $tempdir/workdir/$subdir"); + } + + # It's important that this create an identical tarball each time + # for a given set of input files. So don't include file metadata + # in the tarball, since it can easily vary. + foreach my $file (@manifest) { + if (-l "$tempdir/workdir/$file") { + # Can't set timestamp of a symlink, so + # replace the symlink with an empty file. + unlink("$tempdir/workdir/$file"); + open(OUT, ">$tempdir/workdir/$file"); + close OUT; + } + elsif (! -e "$tempdir/workdir/$file") { + die "$file is listed in the manifest but not present in the source directory.\n"; + } + utime 0, 0, "$tempdir/workdir/$file"; + } + doit("tar", "cf", "$tempdir/gentarball", "--owner", 0, "--group", 0, + "--numeric-owner", "--mode", 0, "-C", "$tempdir/workdir", + "--no-recursion", "--files-from", "$tempdir/manifest"); +} + +sub extract { + my $delta=shift; + my $tarball=shift; + + my $tempdir=tempdir(CLEANUP => 1); + + doit("tar", "xf", File::Spec->rel2abs($delta), "-C", $tempdir); + if (! -e "$tempdir/delta" || ! -e "$tempdir/manifest") { + die "failed to extract delta $delta\n"; + } + + open (IN, "$tempdir/version") || die "delta lacks version number ($!)"; + my $version=; + if ($version > 1) { + die "delta is version $version, not supported\n"; + } + close IN; + + gentarball($tempdir, ".", 0); + doit("xdelta", "patch", "$tempdir/delta", "$tempdir/gentarball", $tarball); + + if ($tarball =~ /(.*)\.gz$/) { + my $base=$1; + if (! rename($tarball, $base)) { + die "failed to rename $tarball to $base"; + } + # --rsyncable might make the deltas use marginally less + # space in revision control. + doit("gzip", "--rsyncable", $base); + } +} + +sub stash { + my $tarball=shift; + my $delta=shift; + + my $tempdir=tempdir(CLEANUP => 1); + + if ($tarball =~ /\.gz$/) { + doit("zcat $tarball > $tempdir/origtarball"); + $tarball="$tempdir/origtarball"; + } + + my $sourcedir="$tempdir/tmp"; + doit("mkdir $sourcedir"); + doit("tar", "xf", File::Spec->rel2abs($tarball), "-C", $sourcedir); + # if all files were in a subdir, use the subdir as the sourcedir + my @out=grep { $_ ne "$sourcedir/.." && $_ ne "$sourcedir/." } + (glob("$sourcedir/*"), glob("$sourcedir/.*")); + if ($#out == 0 && -d $out[0]) { + $sourcedir=$out[0]; + } + + doit("tar tf $tarball > $tempdir/manifest"); + gentarball($tempdir, $sourcedir, 1); + my $ret=system("xdelta delta -0 --pristine $tempdir/gentarball $tarball $tempdir/delta") >> 8; + # xdelta exits 1 on success if there were differences + if ($ret != 1 && $ret != 0) { + die "xdelta failed with return code $ret\n"; + } + + open(OUT, ">", "$tempdir/version") || die "$!"; + print OUT "1.0\n"; + close OUT; + + doit("tar czf $delta -C $tempdir delta manifest version"); +} + +if (! GetOptions("verbose" => \$verbose) || @ARGV != 3) { + usage(); + exit 1; +} + +my $command=shift; +if ($command eq 'extract') { + extract(@ARGV); +} +elsif ($command eq 'stash') { + stash(@ARGV); +} +else { + print STDERR "Unknown subcommand \"$command\"\n"; + usage(); + exit 1; +}