169
use Pristine::Tar::Delta;
170
use Pristine::Tar::Formats;
170
172
use File::Basename;
172
173
use Cwd qw{getcwd abs_path};
174
# magic identification
175
use constant GZIP_ID1 => 0x1F;
176
use constant GZIP_ID2 => 0x8B;
177
use constant BZIP2_ID1 => 0x42;
178
use constant BZIP2_ID2 => 0x5a;
180
# compression methods, 0x00-0x07 are reserved
181
use constant GZIP_METHOD_DEFLATE => 0x08;
183
# compression methods, 'h' for Bzip2 ('H'uffman coding), '0' for Bzip1 (deprecated)
184
use constant BZIP2_METHOD_HUFFMAN => 0x68;
191
175
# Force locale to C since tar may output utf-8 filenames differently
192
176
# depending on the locale.
179
# Don't let environment change tar's behavior.
180
delete $ENV{TAR_OPTIONS};
188
gentar => [\&gentar, 2],
189
gendelta => [\&gendelta, 2],
190
commit => [\&commit],
192
checkout => [\&checkout, 1],
193
co => [\&checkout, 1],
196
"m|message=s" => \$message,
196
201
print STDERR "Usage: pristine-tar [-vdk] gendelta tarball delta\n";
197
202
print STDERR " pristine-tar [-vdk] gentar delta tarball\n";
204
die "pristine-tar: @_\n";
208
print STDERR "pristine-tar: @_\n";
212
message(@_) if $debug;
216
message(@_) if $verbose;
221
if (system(@_) != 0) {
222
error "command failed: @_";
227
return File::Temp::tempdir("pristine-tar.XXXXXXXXXX",
228
TMPDIR => 1, CLEANUP => !$keep);
231
# Workaround for bug #479317 in perl 5.10.
236
208
sub recreatetarball {
209
my $manifestfile=shift;
238
210
my $source=shift;
213
my $tempdir=tempdir();
242
open (IN, "$tempdir/manifest") || die "$tempdir/manifest: $!";
216
open (IN, "<", $manifestfile) || die "$manifestfile: $!";
245
219
push @manifest, $_;
222
link($manifestfile, "$tempdir/manifest") || die "link $tempdir/manifest: $!";
249
224
# The manifest and source should have the same filenames,
250
225
# but the manifest probably has all the files under a common
357
332
my $tarball=shift;
360
my $tempdir=tempdir();
363
$delta="$tempdir/in";
364
open (OUT, ">", $delta) || die "$delta: $!";
371
doit("tar", "xf", File::Spec->rel2abs($delta), "-C", $tempdir);
372
if (! -e "$tempdir/type") {
373
error "failed to gentar delta $delta";
376
open (IN, "$tempdir/version") || error "delta lacks version number ($!)";
378
if ($version >= 3 || $version < 2) {
379
error "delta is version $version, not supported";
382
if (open (IN, "$tempdir/type")) {
385
if ($type ne "tar") {
386
error "delta is for a $type, not a tar";
391
my $recreatetarball=recreatetarball($tempdir, getcwd, clobber_source => 0, %opts);
392
my $out=(-e "$tempdir/wrapper")
393
? $tempdir."/".basename($tarball).".tmp"
395
doit("xdelta", "patch", "$tempdir/delta", $recreatetarball, $out);
397
if (-e "$tempdir/wrapper") {
398
my $type=`tar xOzf $tempdir/wrapper type`;
335
my $delta=Pristine::Tar::Delta::read(Tarball => $deltafile);
336
Pristine::Tar::Delta::assert($delta, type => "tar", maxversion => 2,
337
minversion => 2, fields => [qw{manifest delta}]);
339
my $recreatetarball=recreatetarball($delta->{manifest}, getcwd, clobber_source => 0, %opts);
340
my $out=(defined $delta->{wrapper}
341
? tempdir()."/".basename($tarball).".tmp"
343
doit("xdelta", "patch", $delta->{delta}, $recreatetarball, $out);
345
if (defined $delta->{wrapper}) {
346
my $delta_wrapper=Pristine::Tar::Delta::read(Tarball => $delta->{wrapper});
347
if ($delta_wrapper->{type} eq 'gz') {
401
348
doit("pristine-gz",
402
349
($verbose ? "-v" : "--no-verbose"),
403
350
($debug ? "-d" : "--no-debug"),
404
351
($keep ? "-k" : "--no-keep"),
405
"gengz", "$tempdir/wrapper", $out);
352
"gengz", $delta->{wrapper}, $out);
406
353
doit("mv", "-f", $out.".gz", $tarball);
408
elsif ($type eq 'bz2') {
355
elsif ($delta_wrapper->{type} eq 'bz2') {
409
356
doit("pristine-bz2",
410
357
($verbose ? "-v" : "--no-verbose"),
411
358
($debug ? "-d" : "--no-debug"),
412
359
($keep ? "-k" : "--no-keep"),
413
"genbz2", "$tempdir/wrapper", $out);
360
"genbz2", $delta->{wrapper}, $out);
414
361
doit("mv", "-f", $out.".bz2", $tarball);
417
error "unknown wrapper file type: $type";
364
error "unknown wrapper file type: ".
365
$delta_wrapper->{type};
439
387
my $tarball=shift;
443
391
my $tempdir=tempdir();
448
$delta="$tempdir/out";
451
my @files=qw(delta manifest version type);
453
# Check to see if it's compressed.
394
# Check to see if it's compressed, and get uncompressed tarball.
454
395
my $compression=undef;
455
open (IN, "<", $tarball) || error "Cannot read $tarball: $!\n";
456
my ($chars, $id1, $id2, $method);
457
if (read(IN, $chars, 10) == 10 &&
458
(($id1, $id2, $method) = unpack("CCC", $chars)) &&
459
$id1 == GZIP_ID1 && $id2 == GZIP_ID2 &&
460
$method == GZIP_METHOD_DEFLATE) {
396
if (is_gz($tarball)) {
461
397
$compression='gz';
462
398
open(IN, "-|", "zcat", $tarball) || die "zcat: $!";
463
399
open(OUT, ">", "$tempdir/origtarball") || die "$tempdir/origtarball: $!";
465
401
close IN || die "zcat: $!";
466
402
close OUT || die "$tempdir/origtarball: $!";
469
seek(IN, 0, 0) || die "seek: $!";
470
if (read(IN, $chars, 3) == 3 &&
471
(($id1, $id2, $method) = unpack("CCC", $chars)) &&
472
$id1 == BZIP2_ID1 && $id2 == BZIP2_ID2 &&
473
$method == BZIP2_METHOD_HUFFMAN) {
475
open(IN, "-|", "bzcat", $tarball) || die "bzcat: $!";
476
open(OUT, ">", "$tempdir/origtarball") || die "$tempdir/origtarball: $!";
477
print OUT $_ while <IN>;
478
close IN || die "bzcat: $!";
479
close OUT || die "$tempdir/origtarball: $!";
404
elsif (is_bz2($tarball)) {
406
open(IN, "-|", "bzcat", $tarball) || die "bzcat: $!";
407
open(OUT, ">", "$tempdir/origtarball") || die "$tempdir/origtarball: $!";
408
print OUT $_ while <IN>;
409
close IN || die "bzcat: $!";
410
close OUT || die "$tempdir/origtarball: $!";
484
414
# Generate a wrapper file to recreate the compressed file.
485
415
if (defined $compression) {
416
$delta{wrapper}="$tempdir/wrapper";
486
417
doit("pristine-$compression",
487
418
($verbose ? "-v" : "--no-verbose"),
488
419
($debug ? "-d" : "--no-debug"),
489
420
($keep ? "-k" : "--no-keep"),
490
"gendelta", $tarball, "$tempdir/wrapper");
491
push @files, "wrapper";
421
"gendelta", $tarball, $delta{wrapper});
492
422
$tarball="$tempdir/origtarball";
495
genmanifest($tarball, "$tempdir/manifest");
425
$delta{manifest}="$tempdir/manifest";
426
genmanifest($tarball, $delta{manifest});
496
428
my $recreatetarball;
497
429
if (! exists $opts{recreatetarball}) {
498
430
my $sourcedir="$tempdir/tmp";
504
436
if ($#out == 0 && -d $out[0]) {
505
437
$sourcedir=$out[0];
507
$recreatetarball=recreatetarball($tempdir, $sourcedir, clobber_source => 1);
439
$recreatetarball=recreatetarball("$tempdir/manifest", $sourcedir, clobber_source => 1);
510
442
$recreatetarball=$opts{recreatetarball};
513
my $ret=system("xdelta delta -0 --pristine $recreatetarball $tarball $tempdir/delta") >> 8;
445
$delta{delta}="$tempdir/delta";
446
my $ret=system("xdelta delta -0 --pristine $recreatetarball $tarball $delta{delta}") >> 8;
514
447
# xdelta exits 1 on success if there were differences
515
448
if ($ret != 1 && $ret != 0) {
516
449
error "xdelta failed with return code $ret";
519
open(OUT, ">", "$tempdir/version") || die "$!";
522
open(OUT, ">", "$tempdir/type") || die "$!";
526
doit("tar", "czf", $delta, "-C", $tempdir, @files);
452
Pristine::Tar::Delta::write(Tarball => $deltafile, {
751
677
my $tarball=shift;
678
my $upstream=shift; # optional
680
if (! defined $tarball || @_) {
754
684
my $tempdir=tempdir();
755
685
my ($sourcedir, $id)=export($upstream);
756
686
genmanifest($tarball, "$tempdir/manifest");
757
my $recreatetarball=recreatetarball($tempdir, $sourcedir,
687
my $recreatetarball=recreatetarball("$tempdir/manifest", $sourcedir,
758
688
clobber_source => 1, create_missing => 1);
759
689
my $pid = open(GENDELTA, "-|");
787
717
message("successfully generated $tarball");
790
Getopt::Long::Configure("bundling");
792
"m|message=s" => \$message,
793
"v|verbose!" => \$verbose,
794
"d|debug!" => \$debug,
795
"k|keep!" => \$keep)) {
802
if ($command eq 'gentar') {
803
usage unless @ARGV == 2;
806
elsif ($command eq 'gendelta') {
807
usage unless @ARGV == 2;
810
elsif ($command eq 'commit' || $command eq 'ci') {
811
usage unless @ARGV >= 1;
814
elsif ($command eq 'checkout' || $command eq 'co') {
815
usage unless @ARGV == 1;
819
print STDERR "Unknown subcommand \"$command\"\n";