* Added pristine-gz.
authorjoeyh <joeyh@19660600-52fe-0310-9875-adc0d7a7b53c>
Tue, 2 Oct 2007 13:36:16 +0000 (13:36 +0000)
committerjoeyh <joeyh@19660600-52fe-0310-9875-adc0d7a7b53c>
Tue, 2 Oct 2007 13:36:16 +0000 (13:36 +0000)
* Renamed the subcommands extract => gentar , stash => gendelta

debian/changelog
debian/rules
delta-format.txt [new file with mode: 0644]
pristine-gz [new file with mode: 0755]
pristine-tar

index 1941ffd..538057e 100644 (file)
@@ -1,10 +1,12 @@
 pristine-tar (0.2) UNRELEASED; urgency=low
 
+  * Added pristine-gz.
   * Added bsd-gzip, modified by paravoid to support gzip's (undocumented) -m
     and -M timestamp setting options. This will be needed when regenerating gz
     files created on bsd systems since the compression algorythm differs.
+  * Renamed the subcommands extract => gentar , stash => gendelta
 
- -- Joey Hess <joeyh@debian.org>  Tue, 02 Oct 2007 03:35:25 -0400
+ -- Joey Hess <joeyh@debian.org>  Tue, 02 Oct 2007 09:29:03 -0400
 
 pristine-tar (0.1) unstable; urgency=low
 
index 1006ba2..900adb8 100755 (executable)
@@ -15,7 +15,7 @@ binary-indep: build
        dh_testroot
        dh_clean -k
        dh_install pristine-tar usr/bin
-       dh_installdocs TODO
+       dh_installdocs TODO delta-format.txt
        dh_installman *.1
        dh_installchangelogs
        dh_compress
diff --git a/delta-format.txt b/delta-format.txt
new file mode 100644 (file)
index 0000000..cec7d6b
--- /dev/null
@@ -0,0 +1,32 @@
+The delta file is a compressed tarball, containing the following files:
+
+version
+       Currently "1.0".
+type
+       Type of file this is a delta for ("tar" or "gz").
+       Defaults to "tar" if not present.
+
+
+For tar files, it contains:
+
+manifest
+       List of all files in the tarball, as output by `tar t`.
+       Used to order files correctly when rebuilding it.
+delta
+       xdelta between the generated tarball and the original tarball.
+wrapper
+       Encapsulated delta file for the .gz (or other) wrapper for the
+       tarball. Optional, if not present a pristine .gz won't be generated.
+
+
+For gz files it contains:
+
+program
+       The program to run to recreate the original gz.
+       ("gzip" or "bsd-gzip")
+params
+       Parameters to pass to the program.
+       ("-n 9", "-M", "--rsyncable")
+os
+       OS number from the original gz. Optional, if not present, no OS
+       field munging is done.
diff --git a/pristine-gz b/pristine-gz
new file mode 100755 (executable)
index 0000000..e1ad03c
--- /dev/null
@@ -0,0 +1,179 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+pristine-gz - regenerate pristine gz files
+
+=head1 SYNOPSIS
+
+B<pristine-gz> [-v] gengz delta file
+B<pristine-gz> [-v] gendelta file.gz delta
+
+=head1 DESCRIPTION
+
+This is a complement to the pristine-tar(1) command. Normally you don't
+need to run it by hand, since pristine-tar calls it as necessary to handle
+.tar.gz files.
+
+pristine-gz gendelta takes the specified gz file, and generates a
+small binary delta file that can later be used by pristine-gz gengz
+to recreate the original file.
+
+pristine-gz gengz takes the specified delta file, and compresses
+the specified input file (which must be identical to the contents
+of the original gz file). The resulting gz file will be identical to the
+original gz file.
+
+The approach used to regenerate the original gz file is to figure out how
+it was produced -- what compression level was used, whether it was built
+with gzip(1) or with bsd-gzip(1), whether the --rsyncable option was used,
+etc, and to reproduce this build environment when regenerating the gz.
+In a few cases post-build fixups are also done to ensure that the gz is
+identical to the original.
+
+This approach will work in the vast majority of cases. If it doesn't work,
+no delta will be generated. Please file bug reports.
+
+=head1 OPTIONS
+
+=over 4
+
+=item -v
+
+Verbose mode, show each command that is run.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>,
+Faidon Liambotis <paravoid@debian.org>
+
+Licensed under the GPL, version 2.
+
+=cut
+
+use warnings;
+use strict;
+use File::Temp;
+use File::Path;
+use Getopt::Long;
+
+my $verbose=0;
+
+sub usage {
+       print STDERR "Usage: pristine-gz [-v] gengz delta file\n";
+       print STDERR "       pristine-gz [-v] gendelta file.gz delta\n";
+}
+
+sub vprint {
+       print "pristine-gz: @_\n" if $verbose;
+}
+
+sub doit {
+       vprint(@_);
+       if (system(@_) != 0) {
+               die "command failed: @_\n";
+       }
+}
+
+sub tempdir {
+       return File::Temp::tempdir("pristine-gz.XXXXXXXXXX",
+               TMPDIR => 1, CLEANUP => 1);
+}
+
+sub reproducegz {
+       my $gzfile=shift;
+
+       return "gzip", "-9"; # FIXME
+}
+
+sub gengz {
+       my $delta=shift;
+       my $file=shift;
+
+       my $tempdir=tempdir();
+       
+       doit("tar", "xf", File::Spec->rel2abs($delta), "-C", $tempdir);
+       if (! -e "$tempdir/type") {
+               die "failed to gengz delta $delta\n";
+       }
+
+       open (IN, "$tempdir/version") || die "delta lacks version number ($!)";
+       my $version=<IN>;
+       if ($version >= 2) {
+               die "delta is version $version, not supported\n";
+       }
+       close IN;
+       if (open (IN, "$tempdir/type")) {
+               my $type=<IN>;
+               chomp $type;
+               if ($type ne "gz") {
+                       die "delta is for a $type, not a gz\n";
+               }
+               close IN;
+       }
+
+       
+       open (IN, "$tempdir/program") || die "delta lacks program file ($!)";
+       my $program=<IN>;
+       chomp $program;
+       if ($program !~ /^(gzip|bsd-gzip)$/) {
+               die "paranoia check failed on program file from delta ($program)";
+       }
+       close IN;
+       open (IN, "$tempdir/params") || die "delta lacks params file ($!)";
+       my $params=<IN>;
+       chomp $params;
+       my @params=split(' ', $params);
+       if (grep { ! /^(--no-xfl|--rsyncable|-[nmM1-9])$/ } @params) {
+               die "paranoia check failed on params file from delta (@params)";
+       }
+       close IN;
+
+       doit($program, $params, "-f", $file);
+}
+
+sub gendelta {
+       my $gzfile=shift;
+       my $delta=shift;
+
+       my $tempdir=tempdir();
+       my @files=qw(version type program params);
+
+       my $sourcedir="$tempdir/tmp";
+       doit("mkdir $sourcedir");
+
+       my ($program, $params)=reproducegz($gzfile);
+
+       open(OUT, ">", "$tempdir/version") || die "$!";
+       print OUT "1.1\n";
+       close OUT;
+       open(OUT, ">", "$tempdir/type") || die "$!";
+       print OUT "gz\n";
+       close OUT;
+       open(OUT, ">", "$tempdir/program") || die "$!";
+       print OUT "$program\n";
+       close OUT;
+       open(OUT, ">", "$tempdir/params") || die "$!";
+       print OUT "$params\n";
+       close OUT;
+
+       doit("tar", "czf", $delta, "-C", $tempdir, @files);
+}
+
+if (! GetOptions("verbose!" => \$verbose) || @ARGV != 3) {
+       usage();
+       exit 1;
+}
+
+my $command=shift;
+if ($command eq 'gengz') {
+       gengz(@ARGV);
+}
+elsif ($command eq 'gendelta') {
+       gendelta(@ARGV);
+}
+else {
+       print STDERR "Unknown subcommand \"$command\"\n";
+       usage();
+       exit 1;
+}
index 4d8d904..ebca05f 100755 (executable)
@@ -6,27 +6,27 @@ pristine-tar - regenerate pristine tarballs
 
 =head1 SYNOPSIS
 
-B<pristine-tar> [-v] extract delta tarball
-B<pristine-tar> [-v] stash tarball delta
+B<pristine-tar> [-v] gentar delta tarball
+B<pristine-tar> [-v] gendelta 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
+pristine-tar gendelta takes the specified upstream tarball, and generates a
+small binary delta file that can later be used by pristine-tar gentar
 to recreate the tarball.
 
-pristine-tar extract takes the specified delta file, and the files in the
+pristine-tar gentar 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
+release in revision control, and later gentar 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.
+pristine-tar supports compressed tarballs, calling out to pristine-gz(1)
+to produce pristine gzip files.
 
 =head1 OPTIONS
 
@@ -46,24 +46,33 @@ Licensed under the GPL, version 2 or above.
 
 use warnings;
 use strict;
-use File::Temp qw(tempdir);
+use File::Temp;
 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";
+       print STDERR "Usage: pristine-tar [-v] gentar delta tarball\n";
+       print STDERR "       pristine-tar [-v] gendelta tarball delta\n";
+}
+
+sub vprint {
+       print "pristine-tar: @_\n" if $verbose;
 }
 
 sub doit {
-       print "@_\n" if $verbose;
+       vprint(@_);
        if (system(@_) != 0) {
                die "command failed: @_\n";
        }
 }
 
+sub tempdir {
+       return File::Temp::tempdir("pristine-tar.XXXXXXXXXX",
+               TMPDIR => 1, CLEANUP => 1);
+}
+
 sub gentarball {
        my $tempdir=shift;
        my $source=shift;
@@ -123,45 +132,55 @@ sub gentarball {
                "--no-recursion", "--files-from", "$tempdir/manifest"); 
 }
 
-sub extract {
+sub gentar {
        my $delta=shift;
        my $tarball=shift;
 
-       my $tempdir=tempdir(CLEANUP => 1);
+       my $tempdir=tempdir();
        
        doit("tar", "xf", File::Spec->rel2abs($delta), "-C", $tempdir);
-       if (! -e "$tempdir/delta" || ! -e "$tempdir/manifest") {
-               die "failed to extract delta $delta\n";
+       if (! -e "$tempdir/type") {
+               die "failed to gentar delta $delta\n";
        }
 
        open (IN, "$tempdir/version") || die "delta lacks version number ($!)";
        my $version=<IN>;
-       if ($version > 1) {
+       if ($version >= 2) {
                die "delta is version $version, not supported\n";
        }
        close IN;
+       if (open (IN, "$tempdir/type")) {
+               my $type=<IN>;
+               chomp $type;
+               if ($type ne "tar") {
+                       die "delta is for a $type, not a tar\n";
+               }
+               close IN;
+       }
 
        gentarball($tempdir, ".", 0);
-       doit("xdelta", "patch", "$tempdir/delta", "$tempdir/gentarball", $tarball);
+       my $out=(-e "$tempdir/wrapper") ? $tarball.".tmp" : $tarball;
+       doit("xdelta", "patch", "$tempdir/delta", "$tempdir/gentarball", $out);
 
-       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);
+       if (-e "$tempdir/wrapper") {
+               doit("pristine-gz", ($verbose ? "-v" : "--no-verbose"),
+                       "gengz", "$tempdir/wrapper", $out);
+               doit("mv", $out.".gz", $tarball);
        }
 }
 
-sub stash {
+sub gendelta {
        my $tarball=shift;
        my $delta=shift;
 
-       my $tempdir=tempdir(CLEANUP => 1);
+       my $tempdir=tempdir();
+       my @files=qw(delta manifest version type);
 
-       if ($tarball =~ /\.gz$/) {
+       my $file=`LANG=C file $tarball`;
+       if ($file=~/: gzip compressed data/) {
+               doit("pristine-gz", ($verbose ? "-v" : "--no-verbose"),
+                       "gendelta", $tarball, "$tempdir/wrapper");
+               push @files, "wrapper";
                doit("zcat $tarball > $tempdir/origtarball");
                $tarball="$tempdir/origtarball";
        }
@@ -185,23 +204,26 @@ sub stash {
        }
 
        open(OUT, ">", "$tempdir/version") || die "$!";
-       print OUT "1.0\n";
+       print OUT "1.1\n";
+       close OUT;
+       open(OUT, ">", "$tempdir/type") || die "$!";
+       print OUT "tar\n";
        close OUT;
 
-       doit("tar czf $delta -C $tempdir delta manifest version");
+       doit("tar", "czf", $delta, "-C", $tempdir, @files);
 }
 
-if (! GetOptions("verbose" => \$verbose) || @ARGV != 3) {
+if (! GetOptions("verbose!" => \$verbose) || @ARGV != 3) {
        usage();
        exit 1;
 }
 
 my $command=shift;
-if ($command eq 'extract') {
-       extract(@ARGV);
+if ($command eq 'gentar') {
+       gentar(@ARGV);
 }
-elsif ($command eq 'stash') {
-       stash(@ARGV);
+elsif ($command eq 'gendelta') {
+       gendelta(@ARGV);
 }
 else {
        print STDERR "Unknown subcommand \"$command\"\n";