initial import 0.1
authorjoeyh <joeyh@19660600-52fe-0310-9875-adc0d7a7b53c>
Mon, 1 Oct 2007 00:20:09 +0000 (00:20 +0000)
committerjoeyh <joeyh@19660600-52fe-0310-9875-adc0d7a7b53c>
Mon, 1 Oct 2007 00:20:09 +0000 (00:20 +0000)
TODO [new file with mode: 0644]
debian/changelog [new file with mode: 0644]
debian/compat [new file with mode: 0644]
debian/control [new file with mode: 0644]
debian/copyright [new file with mode: 0644]
debian/rules [new file with mode: 0755]
pristine-tar [new file with mode: 0755]

diff --git a/TODO b/TODO
new file mode 100644 (file)
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 (file)
index 0000000..83c7503
--- /dev/null
@@ -0,0 +1,5 @@
+pristine-tar (0.1) unstable; urgency=low
+
+  * First release.
+
+ -- Joey Hess <joeyh@debian.org>  Sun, 30 Sep 2007 20:06:00 -0400
diff --git a/debian/compat b/debian/compat
new file mode 100644 (file)
index 0000000..7ed6ff8
--- /dev/null
@@ -0,0 +1 @@
+5
diff --git a/debian/control b/debian/control
new file mode 100644 (file)
index 0000000..d5a5264
--- /dev/null
@@ -0,0 +1,20 @@
+Source: pristine-tar
+Section: utils
+Priority: optional
+Build-Depends: debhelper (>= 5), dpkg-dev (>= 1.9.0)
+Maintainer: Joey Hess <joeyh@debian.org>
+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 (file)
index 0000000..2a0b84f
--- /dev/null
@@ -0,0 +1,22 @@
+pristine-tar was written by Joey Hess <joeyh@debian.org>.
+
+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 (executable)
index 0000000..1006ba2
--- /dev/null
@@ -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 (executable)
index 0000000..4d8d904
--- /dev/null
@@ -0,0 +1,210 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+pristine-tar - regenerate pristine tarballs
+
+=head1 SYNOPSIS
+
+B<pristine-tar> [-v] extract delta tarball
+B<pristine-tar> [-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 <joeyh@debian.org>
+
+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 (<IN>) {
+               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=<IN>;
+       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;
+}