Parcourir la source

Merge branch 'sid'

Raphaël Hertzog il y a 16 ans
Parent
commit
a978569f20

+ 19 - 0
debian/changelog

@@ -42,6 +42,25 @@ dpkg (1.15.6) UNRELEASED; urgency=low
 
 
  -- Raphaël Hertzog <hertzog@debian.org>  Tue, 17 Nov 2009 17:24:47 +0100
  -- Raphaël Hertzog <hertzog@debian.org>  Tue, 17 Nov 2009 17:24:47 +0100
 
 
+dpkg (1.15.5.4) unstable; urgency=low
+
+  * Fix Dpkg::Index::get() and remove(). Thanks to Roderich Schupp
+    <roderich.schupp@googlemail.com> for the patch. Closes: #558595
+  * Modify implementation of "3.0 (quilt)" source format to not be
+    behave differently depending on whether quilt is installed or not.
+    The option --without-quilt is thus gone and dpkg-source creates
+    and relies on the .pc directory to know whether patches are applied
+    or not. Closes: #557667
+  * Add new dpkg-source option --single-debian-patch supported by the source
+    format "3.0 (quilt)" so that it behaves more like 1.0 and its single diff
+    that is constantly updated with all upstream changes. Useful if the
+    workflow is VCS based and can't generate a full patch set.
+  * dpkg-source now uses debian/source/patch-header as header of the automatic
+    Debian patch in format "3.0 (quilt)".
+  * Fix Debian changelog parser so that the trailer line is again checked.
+
+ -- Raphael Hertzog <hertzog@debian.org>  Mon, 07 Dec 2009 09:24:31 +0100
+
 dpkg (1.15.5.3) unstable; urgency=low
 dpkg (1.15.5.3) unstable; urgency=low
 
 
   [ Raphael Hertzog ]
   [ Raphael Hertzog ]

+ 37 - 16
man/dpkg-source.1

@@ -394,7 +394,7 @@ such options, and the build is likely to fail.
 .PP
 .PP
 Similarly to quilt's default behaviour, the patches can remove files too.
 Similarly to quilt's default behaviour, the patches can remove files too.
 .PP
 .PP
-The file \fBdebian/patches/.dpkg-source-applied\fP is created if some
+The file \fB.pc/applied-patches\fP is created if some
 patches have been applied during the extraction.
 patches have been applied during the extraction.
 .PP
 .PP
 .B Building
 .B Building
@@ -402,10 +402,14 @@ patches have been applied during the extraction.
 All original tarballs found in the current directory are extracted in a
 All original tarballs found in the current directory are extracted in a
 temporary directory by following the same logic as for the unpack, the
 temporary directory by following the same logic as for the unpack, the
 debian directory is copied over in the temporary directory, and all
 debian directory is copied over in the temporary directory, and all
-patches except \fBdebian-changes-\fP\fIversion\fP are applied.
-The temporary directory is compared to the source package directory
-and the diff (if non-empty) is stored in
-\fBdebian/patches/debian-changes-\fP\fIversion\fP. Any change
+patches except the automatic patch (\fBdebian-changes-\fP\fIversion\fP
+or \fBdebian-changes\fP, depending on \fB\-\-single\-debian\-patch\fP) are
+applied. The temporary directory is compared to the source package
+directory and the diff (if non-empty) is stored in the automatic patch.
+If the automatic patch is created/deleted, it's added/removed from the
+series file and from the quilt metadata.
+
+Any change
 on a binary file is not representable in a diff and will thus lead to a
 on a binary file is not representable in a diff and will thus lead to a
 failure unless the maintainer deliberately decided to include that
 failure unless the maintainer deliberately decided to include that
 modified binary file in the debian tarball (by listing it in
 modified binary file in the debian tarball (by listing it in
@@ -428,17 +432,23 @@ This is not the case when the source tree has been obtained by unpacking a
 source package using the Format: 1.0 for instance. To mitigate the
 source package using the Format: 1.0 for instance. To mitigate the
 problem, \fBdpkg\-source\fP will apply the patches by itself if it
 problem, \fBdpkg\-source\fP will apply the patches by itself if it
 believes that they have not yet been applied. To detect this situation, it
 believes that they have not yet been applied. To detect this situation, it
-uses the following heuristic: if a \fB.pc\fP subdirectory is detected, it
-will call \fBquilt unapplied\fP to find out if some patches are not
-applied. After that, it will take the first patch of the series (or the
-first unapplied patch returned by \fBquilt unapplied\fP, if any) and
-verify if it can be applied without errors. If the verification succeeds,
-it concludes that patches have not been applied and will apply them all.
+uses the following heuristic: it finds the list of supposedly unapplied
+patches (they are listed in the \fBseries\fP file but not in
+\fB.pc/applied-patches\fP), and if the first patch in that set can be
+applied without errors, it will apply them all.
 The option \fB\-\-no\-preparation\fP can be used to disable this
 The option \fB\-\-no\-preparation\fP can be used to disable this
 behaviour.
 behaviour.
 .PP
 .PP
 .B Build options
 .B Build options
 .TP
 .TP
+.BI \-\-allow\-version\-of\-quilt\-db= version
+Allow \fBdpkg\-source\fP to build the source package if the version of
+the quilt metadata is the one specified, even if \fBdpkg\-source\fP
+doesn't know about it. Effectively this says that the given version of the
+quilt metadata is compatible with the version 2 that \fBdpkg\-source\fP
+currently supports. The version of the quilt metadata is stored in
+\fB.pc/.version\fP.
+.TP
 .B \-\-include\-removal
 .B \-\-include\-removal
 Do not ignore removed files and include them in the automatically
 Do not ignore removed files and include them in the automatically
 generated patch.
 generated patch.
@@ -454,6 +464,17 @@ in subsequent builds and this option is thus no more needed.
 .B \-\-no\-preparation
 .B \-\-no\-preparation
 Do not try to prepare the build tree by applying patches which are
 Do not try to prepare the build tree by applying patches which are
 apparently unapplied.
 apparently unapplied.
+.TP
+.B \-\-single\-debian\-patch
+Use \fBdebian/patches/debian-changes\fP instead of
+\fBdebian/patches/debian-changes-\fP\fIversion\fP for the name of the
+automatic patch generated during build. This option is particularly
+useful when the package is maintained in a VCS and a patch set can't reliably
+be generated. Instead the current diff with upstream should be stored in a
+single patch. When using this option, it is recommended to create
+a debian/source/patch-header file explaining how the Debian changes can be
+best reviewed, for example in the VCS that is used.
+
 .PP
 .PP
 .B Extract options
 .B Extract options
 .TP
 .TP
@@ -462,11 +483,6 @@ Skips extraction of the debian tarball on top of the upstream sources.
 .TP
 .TP
 .B \-\-skip\-patches
 .B \-\-skip\-patches
 Do not apply patches at the end of the extraction.
 Do not apply patches at the end of the extraction.
-.TP
-.B \-\-without\-quilt
-Don't use quilt to apply patches but dpkg-source's own code. It won't be
-possible to use quilt directly on the unpacked directory but it will be
-free of quilt's temporary files as well.
 .
 .
 .SS Format: 3.0 (custom)
 .SS Format: 3.0 (custom)
 This format is particular. It doesn't represent a real source package
 This format is particular. It doesn't represent a real source package
@@ -525,9 +541,14 @@ Here's an example of such a file:
   # let dpkg-source create a debian.tar.bz2 with maximal compression
   # let dpkg-source create a debian.tar.bz2 with maximal compression
   compression = "bzip2"
   compression = "bzip2"
   compression-level = 9
   compression-level = 9
+  # use debian/patches/debian-changes as automatic patch
+  single-debian-patch
 .P
 .P
 Note: \fBformat\fR options are not accepted in this file, you should
 Note: \fBformat\fR options are not accepted in this file, you should
 use \fBdebian/source/format\fR instead.
 use \fBdebian/source/format\fR instead.
+.SS debian/source/patch-header
+Free form text that is put on top of the automatic patch generated
+in formats "2.0" or "3.0 (quilt)".
 .SS debian/patches/series
 .SS debian/patches/series
 This file lists all patches that have to be applied (in the given order)
 This file lists all patches that have to be applied (in the given order)
 on top of the upstream source package. Leading and trailing spaces are
 on top of the upstream source package. Leading and trailing spaces are

+ 1 - 1
scripts/Dpkg/Changelog/Debian.pm

@@ -128,7 +128,7 @@ sub parse {
 	    $entry->set_part("trailer", $_);
 	    $entry->set_part("trailer", $_);
 	    $entry->extend_part("blank_after_changes", [ @blanklines ]);
 	    $entry->extend_part("blank_after_changes", [ @blanklines ]);
 	    @blanklines = ();
 	    @blanklines = ();
-	    foreach my $error ($entry->check_header()) {
+	    foreach my $error ($entry->check_trailer()) {
 		$self->parse_error($file, $., $error, $_);
 		$self->parse_error($file, $., $error, $_);
 	    }
 	    }
 	    $expect = NEXT_OR_EOF;
 	    $expect = NEXT_OR_EOF;

+ 2 - 2
scripts/Dpkg/Index.pm

@@ -262,7 +262,7 @@ Returns all the items that matches all the criteria.
 
 
 sub get {
 sub get {
     my ($self, %crit) = @_;
     my ($self, %crit) = @_;
-    return map { $self->{'items'}{$_} } $self->get_by_keys(%crit);
+    return map { $self->{'items'}{$_} } $self->get_keys(%crit);
 }
 }
 
 
 =item $index->remove_by_key($key)
 =item $index->remove_by_key($key)
@@ -285,7 +285,7 @@ Returns and removes all the items that matches all the criteria.
 
 
 sub remove {
 sub remove {
     my ($self, %crit) = @_;
     my ($self, %crit) = @_;
-    my @keys = $self->get_by_keys(%crit);
+    my @keys = $self->get_keys(%crit);
     my (%keys, @ret);
     my (%keys, @ret);
     foreach my $key (@keys) {
     foreach my $key (@keys) {
 	$keys{$key} = 1;
 	$keys{$key} = 1;

+ 20 - 9
scripts/Dpkg/Source/Package/V2.pm

@@ -151,7 +151,8 @@ sub do_extract {
                   @exclude_symlinks ]);
                   @exclude_symlinks ]);
 
 
     # Apply patches (in a separate method as it might be overriden)
     # Apply patches (in a separate method as it might be overriden)
-    $self->apply_patches($newdirectory) unless $self->{'options'}{'skip_patches'};
+    $self->apply_patches($newdirectory, usage => 'unpack')
+        unless $self->{'options'}{'skip_patches'};
 }
 }
 
 
 sub get_autopatch_name {
 sub get_autopatch_name {
@@ -159,7 +160,8 @@ sub get_autopatch_name {
 }
 }
 
 
 sub get_patches {
 sub get_patches {
-    my ($self, $dir, $skip_auto) = @_;
+    my ($self, $dir, %opts) = @_;
+    $opts{"skip_auto"} = 0 unless defined($opts{"skip_auto"});
     my @patches;
     my @patches;
     my $pd = "$dir/debian/patches";
     my $pd = "$dir/debian/patches";
     my $auto_patch = $self->get_autopatch_name();
     my $auto_patch = $self->get_autopatch_name();
@@ -168,7 +170,7 @@ sub get_patches {
         foreach my $patch (sort readdir(DIR)) {
         foreach my $patch (sort readdir(DIR)) {
             # patches match same rules as run-parts
             # patches match same rules as run-parts
             next unless $patch =~ /^[\w-]+$/ and -f "$pd/$patch";
             next unless $patch =~ /^[\w-]+$/ and -f "$pd/$patch";
-            next if $skip_auto and $patch eq $auto_patch;
+            next if $opts{"skip_auto"} and $patch eq $auto_patch;
             push @patches, $patch;
             push @patches, $patch;
         }
         }
         closedir(DIR);
         closedir(DIR);
@@ -177,15 +179,16 @@ sub get_patches {
 }
 }
 
 
 sub apply_patches {
 sub apply_patches {
-    my ($self, $dir, $skip_auto) = @_;
-    my @patches = $self->get_patches($dir, $skip_auto);
+    my ($self, $dir, %opts) = @_;
+    $opts{"skip_auto"} = 0 unless defined($opts{"skip_auto"});
+    my @patches = $self->get_patches($dir, %opts);
     return unless scalar(@patches);
     return unless scalar(@patches);
     my $timestamp = time();
     my $timestamp = time();
     my $applied = File::Spec->catfile($dir, "debian", "patches", ".dpkg-source-applied");
     my $applied = File::Spec->catfile($dir, "debian", "patches", ".dpkg-source-applied");
     open(APPLIED, '>', $applied) || syserr(_g("cannot write %s"), $applied);
     open(APPLIED, '>', $applied) || syserr(_g("cannot write %s"), $applied);
-    foreach my $patch ($self->get_patches($dir, $skip_auto)) {
+    foreach my $patch ($self->get_patches($dir, %opts)) {
         my $path = File::Spec->catfile($dir, "debian", "patches", $patch);
         my $path = File::Spec->catfile($dir, "debian", "patches", $patch);
-        info(_g("applying %s"), $patch) unless $skip_auto;
+        info(_g("applying %s"), $patch) unless $opts{"skip_auto"};
         my $patch_obj = Dpkg::Source::Patch->new(filename => $path);
         my $patch_obj = Dpkg::Source::Patch->new(filename => $path);
         $patch_obj->apply($dir, force_timestamp => 1,
         $patch_obj->apply($dir, force_timestamp => 1,
                           timestamp => $timestamp,
                           timestamp => $timestamp,
@@ -220,7 +223,7 @@ sub check_patches_applied {
     my $applied = File::Spec->catfile($dir, "debian", "patches", ".dpkg-source-applied");
     my $applied = File::Spec->catfile($dir, "debian", "patches", ".dpkg-source-applied");
     unless (-e $applied) {
     unless (-e $applied) {
         warning(_g("patches have not been applied, applying them now (use --no-preparation to override)"));
         warning(_g("patches have not been applied, applying them now (use --no-preparation to override)"));
-        $self->apply_patches($dir);
+        $self->apply_patches($dir, usage => 'preparation');
     }
     }
 }
 }
 
 
@@ -287,7 +290,7 @@ sub do_build {
     subprocerr(_g("copy of the debian directory")) if $?;
     subprocerr(_g("copy of the debian directory")) if $?;
 
 
     # Apply all patches except the last automatic one
     # Apply all patches except the last automatic one
-    $self->apply_patches($tmp, 1);
+    $self->apply_patches($tmp, skip_auto => 1, usage => 'build');
 
 
     # Prepare handling of binary files
     # Prepare handling of binary files
     my %auth_bin_files;
     my %auth_bin_files;
@@ -428,6 +431,14 @@ sub do_build {
 
 
 sub get_patch_header {
 sub get_patch_header {
     my ($self, $dir, $previous) = @_;
     my ($self, $dir, $previous) = @_;
+    my $ph = File::Spec->catfile($dir, "debian", "source", "patch-header");
+    my $text;
+    if (-f $ph) {
+        open(PH, "<", $ph) || syserr(_g("cannot read %s"), $ph);
+        $text = join("", <PH>);
+        close(PH);
+        return $text;
+    }
     return "Description: Undocumented upstream changes
     return "Description: Undocumented upstream changes
  This patch has been created by dpkg-source during the package build
  This patch has been created by dpkg-source during the package build
  but it might have accumulated changes from several uploads. Please
  but it might have accumulated changes from several uploads. Please

+ 172 - 159
scripts/Dpkg/Source/Package/V3/quilt.pm

@@ -1,4 +1,4 @@
-# Copyright © 2008 Raphaël Hertzog <hertzog@debian.org>
+# Copyright © 2008-2009 Raphaël Hertzog <hertzog@debian.org>
 #
 #
 # This program is free software; you can redistribute it and/or modify
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -25,6 +25,7 @@ use Dpkg;
 use Dpkg::Gettext;
 use Dpkg::Gettext;
 use Dpkg::ErrorHandling;
 use Dpkg::ErrorHandling;
 use Dpkg::Source::Patch;
 use Dpkg::Source::Patch;
+use Dpkg::Source::Functions qw(erasedir);
 use Dpkg::IPC;
 use Dpkg::IPC;
 use Dpkg::Vendor qw(get_current_vendor run_vendor_hook);
 use Dpkg::Vendor qw(get_current_vendor run_vendor_hook);
 use Dpkg::Control;
 use Dpkg::Control;
@@ -39,17 +40,22 @@ our $CURRENT_MINOR_VERSION = "0";
 
 
 sub init_options {
 sub init_options {
     my ($self) = @_;
     my ($self) = @_;
+    $self->{'options'}{'single-debian-patch'} = 0
+        unless exists $self->{'options'}{'single-debian-patch'};
+    $self->{'options'}{'allow-version-of-quilt-db'} = []
+        unless exists $self->{'options'}{'allow-version-of-quilt-db'};
+
     $self->SUPER::init_options();
     $self->SUPER::init_options();
-    # By default use quilt, unless it's not available
-    $self->{'options'}{'without_quilt'} = (-x "/usr/bin/quilt") ? 0 : 1
-        unless exists $self->{'options'}{'without_quilt'};
 }
 }
 
 
 sub parse_cmdline_option {
 sub parse_cmdline_option {
     my ($self, $opt) = @_;
     my ($self, $opt) = @_;
     return 1 if $self->SUPER::parse_cmdline_option($opt);
     return 1 if $self->SUPER::parse_cmdline_option($opt);
-    if ($opt =~ /^--without-quilt$/) {
-        $self->{'options'}{'without_quilt'} = 1;
+    if ($opt =~ /^--single-debian-patch$/) {
+        $self->{'options'}{'single-debian-patch'} = 1;
+        return 1;
+    } elsif ($opt =~ /^--allow-version-of-quilt-db=(.*)$/) {
+        push @{$self->{'options'}{'allow-version-of-quilt-db'}}, $1;
         return 1;
         return 1;
     }
     }
     return 0;
     return 0;
@@ -75,7 +81,11 @@ sub can_build {
 
 
 sub get_autopatch_name {
 sub get_autopatch_name {
     my ($self) = @_;
     my ($self) = @_;
-    return "debian-changes-" . $self->{'fields'}{'Version'};
+    if ($self->{'options'}{'single-debian-patch'}) {
+        return "debian-changes";
+    } else {
+        return "debian-changes-" . $self->{'fields'}{'Version'};
+    }
 }
 }
 
 
 sub get_series_file {
 sub get_series_file {
@@ -89,63 +99,80 @@ sub get_series_file {
     return undef;
     return undef;
 }
 }
 
 
-sub get_patches {
-    my ($self, $dir, $skip_auto) = @_;
+sub read_patch_list {
+    my ($self, $file, %opts) = @_;
+    return () if not defined $file or not -f $file;
+    $opts{"warn_options"} = 0 unless defined($opts{"warn_options"});
+    $opts{"skip_auto"} = 0 unless defined($opts{"skip_auto"});
     my @patches;
     my @patches;
     my $auto_patch = $self->get_autopatch_name();
     my $auto_patch = $self->get_autopatch_name();
-    my $series = $self->get_series_file($dir);
-    if (defined($series)) {
-        open(SERIES, "<" , $series) || syserr(_g("cannot read %s"), $series);
-        while(defined($_ = <SERIES>)) {
-            chomp; s/^\s+//; s/\s+$//; # Strip leading/trailing spaces
-            s/(^|\s+)#.*$//; # Strip comment
-            next unless $_;
-            if (/^(\S+)\s+(.*)$/) {
-                $_ = $1;
-                if ($2 ne '-p1') {
-                    warning(_g("the series file (%s) contains unsupported " .
-                               "options ('%s', line %s), dpkg-source might " .
-                               "fail when applying patches."),
-                            $series, $2, $.) unless $skip_auto;
-                }
+    open(SERIES, "<" , $file) || syserr(_g("cannot read %s"), $file);
+    while(defined($_ = <SERIES>)) {
+        chomp; s/^\s+//; s/\s+$//; # Strip leading/trailing spaces
+        s/(^|\s+)#.*$//; # Strip comment
+        next unless $_;
+        if (/^(\S+)\s+(.*)$/) {
+            $_ = $1;
+            if ($2 ne '-p1') {
+                warning(_g("the series file (%s) contains unsupported " .
+                           "options ('%s', line %s), dpkg-source might " .
+                           "fail when applying patches."),
+                        $file, $2, $.) if $opts{"warn_options"};
             }
             }
-            next if $skip_auto and $_ eq $auto_patch;
-            push @patches, $_;
         }
         }
-        close(SERIES);
+        next if $opts{"skip_auto"} and $_ eq $auto_patch;
+        push @patches, $_;
     }
     }
+    close(SERIES);
     return @patches;
     return @patches;
 }
 }
 
 
-sub run_quilt {
-    my ($self, $dir, $params, %more_opts) = @_;
-    $params = [ $params ] unless ref($params) eq "ARRAY";
-    my $absdir = $dir;
-    unless (File::Spec->file_name_is_absolute($absdir)) {
-        $absdir = File::Spec->rel2abs($dir);
+sub create_quilt_db {
+    my ($self, $dir) = @_;
+    my $db_dir = File::Spec->catdir($dir, ".pc");
+    if (not -d $db_dir) {
+        mkdir $db_dir or syserr(_g("cannot mkdir %s"), $db_dir);
     }
     }
-    my $series = $self->get_series_file($dir);
-    # Use default name if no series files exist yet
-    $series = "$absdir/debian/patches/series" unless defined $series;
-    unless (File::Spec->file_name_is_absolute($series)) {
-        $series = File::Spec->rel2abs($series);
+    my $version_file = File::Spec->catfile($db_dir, ".version");
+    if (not -e $version_file) {
+        open(VERSION, ">", $version_file);
+        print VERSION "2\n";
+        close(VERSION);
     }
     }
-    my %opts = (
-        env => { QUILT_PATCHES => "$absdir/debian/patches",
-                 QUILT_SERIES => $series,
-                 # Kept as close as possible to default patch options in
-                 # Dpkg::Source::Patch (used in without_quilt mode)
-                 QUILT_PATCH_OPTS => "-t -F 0 -N -u -V never -g0" },
-        'chdir' => $dir,
-        'exec' => [ 'quilt', '--quiltrc', '/dev/null', @$params ],
-        %more_opts
-    );
-    my $pid = fork_and_exec(%opts);
-    return $pid;
+}
+
+sub apply_quilt_patch {
+    my ($self, $dir, $patch, %opts) = @_;
+    $opts{"verbose"} = 0 unless defined($opts{"verbose"});
+    $opts{"timestamp"} = time() unless defined($opts{"timestamp"});
+    my $path = File::Spec->catfile($dir, "debian", "patches", $patch);
+    my $obj = Dpkg::Source::Patch->new(filename => $path);
+
+    info(_g("applying %s"), $patch) if $opts{"verbose"};
+    $obj->apply($dir, timestamp => $opts{"timestamp"},
+                force_timestamp => 1, create_dirs => 1, remove_backup => 0,
+                options => [ '-s', '-t', '-F', '0', '-N', '-p1', '-u',
+                             '-V', 'never', '-g0', '-E', '-b',
+                             '-B', ".pc/$patch/" ]);
+}
+
+sub get_patches {
+    my ($self, $dir, %opts) = @_;
+    my $series = $self->get_series_file($dir);
+    return $self->read_patch_list($series, %opts);
 }
 }
 
 
 sub apply_patches {
 sub apply_patches {
-    my ($self, $dir, $skip_auto) = @_;
+    my ($self, $dir, %opts) = @_;
+
+    if ($opts{'usage'} eq 'unpack') {
+        $opts{'verbose'} = 1;
+    } elsif ($opts{'usage'} eq 'build') {
+        $opts{'warn_options'} = 1;
+        $opts{'verbose'} = 0;
+    }
+
+    my $patches = $opts{"patches"};
 
 
     # Update debian/patches/series symlink if needed to allow quilt usage
     # Update debian/patches/series symlink if needed to allow quilt usage
     my $series = $self->get_series_file($dir);
     my $series = $self->get_series_file($dir);
@@ -159,45 +186,22 @@ sub apply_patches {
                 syserr(_g("can't create symlink %s"), $dest);
                 syserr(_g("can't create symlink %s"), $dest);
         }
         }
     }
     }
-    my @patches = $self->get_patches($dir, $skip_auto);
-    return unless scalar(@patches);
 
 
-    # Apply patches
-    my $applied = File::Spec->catfile($dir, "debian", "patches", ".dpkg-source-applied");
-    open(APPLIED, '>', $applied) || syserr(_g("cannot write %s"), $applied);
-    my $now = time();
-    my $pobj = {};
-    my $panalysis = {};
-    foreach my $patch (@patches) {
-        my $path = File::Spec->catfile($dir, "debian", "patches", $patch);
-        $pobj->{$patch} = Dpkg::Source::Patch->new(filename => $path);
-        if ($self->{'options'}{'without_quilt'}) {
-            info(_g("applying %s"), $patch) unless $skip_auto;
-            $pobj->{$patch}->apply($dir, timestamp => $now,
-                    force_timestamp => 1, create_dirs => 1,
-                    add_options => [ '-E' ]);
-            print APPLIED "$patch\n";
-        } else {
-            $panalysis->{$patch} = $pobj->{$patch}->analyze($dir);
-            foreach my $dir (keys %{$panalysis->{$patch}->{'dirtocreate'}}) {
-                eval { mkpath($dir); };
-                syserr(_g("cannot create directory %s"), $dir) if $@;
-            }
-        }
+    unless (defined($patches)) {
+        $patches = [ $self->get_patches($dir, %opts) ];
     }
     }
-    if (not $self->{'options'}{'without_quilt'}) {
-        my %opts;
-        $opts{"to_file"} = "/dev/null" if $skip_auto;
-        info(_g("applying all patches with %s"), "quilt push -q " . $patches[-1]) unless $skip_auto;
-        $self->run_quilt($dir, ['push', '-q', $patches[-1]],
-                         wait_child => 1, %opts);
-        foreach my $patch (@patches) {
-            foreach my $fn (keys %{$panalysis->{$patch}->{'filepatched'}}) {
-                utime($now, $now, $fn) || $! == ENOENT ||
-                    syserr(_g("cannot change timestamp for %s"), $fn);
-            }
-            print APPLIED "$patch\n";
-        }
+    return unless scalar(@$patches);
+
+    # Apply patches
+    $self->create_quilt_db($dir);
+    my $pc_applied = File::Spec->catfile($dir, ".pc", "applied-patches");
+    my @applied = $self->read_patch_list($pc_applied);
+    my @patches = $self->read_patch_list($self->get_series_file($dir));
+    open(APPLIED, '>>', $pc_applied) || syserr(_g("cannot write %s"), $pc_applied);
+    $opts{"timestamp"} = time();
+    foreach my $patch (@$patches) {
+        $self->apply_quilt_patch($dir, $patch, %opts);
+        print APPLIED "$patch\n";
     }
     }
     close(APPLIED);
     close(APPLIED);
 }
 }
@@ -210,7 +214,6 @@ sub prepare_build {
     # stamp file created by ourselves
     # stamp file created by ourselves
     my $func = sub {
     my $func = sub {
         return 1 if $_[0] =~ m{^debian/patches/series$} and -l $_[0];
         return 1 if $_[0] =~ m{^debian/patches/series$} and -l $_[0];
-        return 1 if $_[0] =~ m{^debian/patches/.dpkg-source-applied$};
         return 1 if $_[0] =~ /^.pc(\/|$)/;
         return 1 if $_[0] =~ /^.pc(\/|$)/;
         return 1 if $_[0] =~ /$self->{'options'}{'diff_ignore_regexp'}/;
         return 1 if $_[0] =~ /$self->{'options'}{'diff_ignore_regexp'}/;
         return 0;
         return 0;
@@ -218,99 +221,99 @@ sub prepare_build {
     $self->{'diff_options'}{'diff_ignore_func'} = $func;
     $self->{'diff_options'}{'diff_ignore_func'} = $func;
 }
 }
 
 
+sub do_build {
+    my ($self, $dir) = @_;
+    my $pc_ver = File::Spec->catfile($dir, ".pc", ".version");
+    if (-f $pc_ver) {
+        open(VER, "<", $pc_ver) || syserr(_g("cannot read %s"), $pc_ver);
+        my $version = <VER>;
+        chomp $version;
+        close(VER);
+        if ($version != 2) {
+            if (scalar grep { $version eq $_ }
+                @{$self->{'options'}{'allow-version-of-quilt-db'}})
+            {
+                warning(_g("unsupported version of the quilt metadata: %s"),
+                        $version);
+            } else {
+                error(_g("unsupported version of the quilt metadata: %s"),
+                      $version);
+            }
+        }
+    }
+    $self->SUPER::do_build($dir);
+}
+
 sub check_patches_applied {
 sub check_patches_applied {
     my ($self, $dir) = @_;
     my ($self, $dir) = @_;
-    my $applied = File::Spec->catfile($dir, "debian", "patches", ".dpkg-source-applied");
-    my $auto_patch = $self->get_autopatch_name();
-    my @patches ;
-    # First we try to get a list of patches that are probably not napplied
-    if (not $self->{'options'}{'without_quilt'}) {
-        my $pipe;
-        my $pid = $self->run_quilt($dir, ['unapplied'], error_to_file => '/dev/null',
-                                   to_pipe => \$pipe);
-        @patches = map { chomp; $_ } (<$pipe>);
-        close ($pipe) || syserr("close on 'quilt unapplied' pipe");
-        wait_child($pid, cmdline => "quilt unapplied", nocheck => 1);
-        subprocerr("quilt unapplied") unless WIFEXITED($?);
-    } else {
-        @patches = $self->get_patches($dir);
+    my $pc_applied = File::Spec->catfile($dir, ".pc", "applied-patches");
+    my @applied = $self->read_patch_list($pc_applied);
+    my @patches = $self->read_patch_list($self->get_series_file($dir));
+    my @to_apply;
+    foreach my $patch (@patches) {
+        next if scalar grep { $_ eq $patch } @applied;
+        push @to_apply, $patch;
     }
     }
-    # Then we check if it's applicable, and if yes, we make the
-    # assumption that patches are not applied and need to be applied
-    if (scalar(@patches)) {
-        my $first_patch = File::Spec->catfile($dir, "debian", "patches", $patches[0]);
+    if (scalar(@to_apply)) {
+        my $first_patch = File::Spec->catfile($dir, "debian", "patches",
+                                              $to_apply[0]);
         my $patch_obj = Dpkg::Source::Patch->new(filename => $first_patch);
         my $patch_obj = Dpkg::Source::Patch->new(filename => $first_patch);
         if ($patch_obj->check_apply($dir)) {
         if ($patch_obj->check_apply($dir)) {
-            warning(_g("patches have not been applied, applying them now (use --no-preparation to override)"));
-            $self->apply_patches($dir);
+            warning(_g("patches have not been applied, applying them now " .
+                       "(use --no-preparation to override)"));
+            $self->apply_patches($dir, usage => 'preparation', verbose => 1,
+                                 patches => \@to_apply);
         }
         }
     }
     }
 }
 }
 
 
 sub register_autopatch {
 sub register_autopatch {
     my ($self, $dir) = @_;
     my ($self, $dir) = @_;
+
+    sub add_line {
+        my ($file, $line) = @_;
+        open(FILE, ">>", $file) || syserr(_g("cannot write %s"), $file);
+        print FILE "$line\n";
+        close(FILE);
+    }
+
+    sub drop_line {
+        my ($file, $re) = @_;
+        open(FILE, "<", $file) || syserr(_g("cannot read %s"), $file);
+        my @lines = <FILE>;
+        close(FILE);
+        open(FILE, ">", $file) || syserr(_g("cannot write %s"), $file);
+        print(FILE $_) foreach grep { not /^\Q$re\E\s*$/ } @lines;
+        close(FILE);
+    }
+
     my $auto_patch = $self->get_autopatch_name();
     my $auto_patch = $self->get_autopatch_name();
     my @patches = $self->get_patches($dir);
     my @patches = $self->get_patches($dir);
     my $has_patch = (grep { $_ eq $auto_patch } @patches) ? 1 : 0;
     my $has_patch = (grep { $_ eq $auto_patch } @patches) ? 1 : 0;
     my $series = $self->get_series_file($dir);
     my $series = $self->get_series_file($dir);
     $series ||= File::Spec->catfile($dir, "debian", "patches", "series");
     $series ||= File::Spec->catfile($dir, "debian", "patches", "series");
-    my $applied = File::Spec->catfile($dir, "debian", "patches", ".dpkg-source-applied");
+    my $applied = File::Spec->catfile($dir, ".pc", "applied-patches");
     my $patch = File::Spec->catfile($dir, "debian", "patches", $auto_patch);
     my $patch = File::Spec->catfile($dir, "debian", "patches", $auto_patch);
-    my $absdir = $dir;
-    unless (File::Spec->file_name_is_absolute($absdir)) {
-        $absdir = File::Spec->rel2abs($dir);
-    }
+
     if (-e $patch) {
     if (-e $patch) {
         # Add auto_patch to series file
         # Add auto_patch to series file
         if (not $has_patch) {
         if (not $has_patch) {
-            # Use quilt to register only if it's wanted/available AND :
-            # - either we have patches and quilt has been used (.pc dir exists)
-            # - or we don't have patches, hence quilt couldn't be used
-            if ((-d "$dir/.pc" or not scalar(@patches)) and
-                not $self->{'options'}{'without_quilt'})
-            {
-                # Registering the new patch with quilt requires some
-                # trickery: reverse-apply the patch, create a new quilt patch,
-                # fold the patch into the quilt-managed one
-                my $patch_obj = Dpkg::Source::Patch->new(filename => $patch);
-                $patch_obj->apply($dir, add_options => ['-R', '-E']);
-                $self->run_quilt($dir, ['new', "$auto_patch"],
-                                 wait_child => 1, to_file => '/dev/null');
-                $self->run_quilt($dir, ['fold'],
-                                 from_file => "$absdir/debian/patches/$auto_patch",
-                                 wait_child => 1, to_file => '/dev/null');
-            } else {
-                open(SERIES, ">>", $series) || syserr(_g("cannot write %s"), $series);
-                print SERIES "$auto_patch\n";
-                close(SERIES);
-            }
-        } else {
-            # If quilt was used, ensure its meta-information are
-            # synchronized with the updated patch
-            if (-d "$dir/.pc" and not $self->{'options'}{'without_quilt'}) {
-                # Some trickery needed: reverse-apply the patch, fold the
-                # new patch into the quilt-managed one
-                my $patch_obj = Dpkg::Source::Patch->new(filename => $patch);
-                $patch_obj->apply($dir, add_options => ['-R', '-E']);
-                $self->run_quilt($dir, ['fold'],
-                                 from_file => "$absdir/debian/patches/$auto_patch",
-                                 wait_child => 1, to_file => '/dev/null');
-            }
+            add_line($series, $auto_patch);
+            add_line($applied, $auto_patch);
         }
         }
+        # Ensure quilt meta-data are created and in sync with some trickery:
+        # reverse-apply the patch, drop .pc/$patch, re-apply it
+        # with the correct options to recreate the backup files
+        my $patch_obj = Dpkg::Source::Patch->new(filename => $patch);
+        $patch_obj->apply($dir, add_options => ['-R', '-E']);
+        erasedir(File::Spec->catdir($dir, ".pc", $auto_patch));
+        $self->apply_quilt_patch($dir, $auto_patch);
     } else {
     } else {
         # Remove auto_patch from series
         # Remove auto_patch from series
         if ($has_patch) {
         if ($has_patch) {
-            if ($self->{'options'}{'without_quilt'}) {
-                open(SERIES, "<", $series) || syserr(_g("cannot read %s"), $series);
-                my @lines = <SERIES>;
-                close(SERIES);
-                open(SERIES, ">", $series) || syserr(_g("cannot write %s"), $series);
-                print(SERIES $_) foreach grep { not /^\Q$auto_patch\E\s*$/ } @lines;
-                close(SERIES);
-            } else {
-                $self->run_quilt($dir, ['delete', $auto_patch],
-                                 wait_child => 1, to_file => '/dev/null');
-            }
+            drop_line($series, $auto_patch);
+            drop_line($applied, $auto_patch);
+            erasedir(File::Spec->catdir($dir, ".pc", $auto_patch));
         }
         }
         # Clean up empty series
         # Clean up empty series
         unlink($series) if not -s $series;
         unlink($series) if not -s $series;
@@ -319,9 +322,19 @@ sub register_autopatch {
 
 
 sub get_patch_header {
 sub get_patch_header {
     my ($self, $dir, $previous) = @_;
     my ($self, $dir, $previous) = @_;
+    my $ph = File::Spec->catfile($dir, "debian", "source", "patch-header");
+    my $text;
+    if (-f $ph) {
+        open(PH, "<", $ph) || syserr(_g("cannot read %s"), $ph);
+        $text = join("", <PH>);
+        close(PH);
+        return $text;
+    }
     my $ch_info = changelog_parse(offset => 0, count => 1,
     my $ch_info = changelog_parse(offset => 0, count => 1,
         file => File::Spec->catfile($dir, "debian", "changelog"));
         file => File::Spec->catfile($dir, "debian", "changelog"));
     return '' if not defined $ch_info;
     return '' if not defined $ch_info;
+    return $self->SUPER::get_patch_header($dir, $previous)
+        if $self->{'options'}{'single-debian-patch'};
     my $header = Dpkg::Control->new(type => CTRL_UNKNOWN);
     my $header = Dpkg::Control->new(type => CTRL_UNKNOWN);
     $header->{'Description'} = "Upstream changes introduced in version " .
     $header->{'Description'} = "Upstream changes introduced in version " .
                                $ch_info->{'Version'} . "\n";
                                $ch_info->{'Version'} . "\n";
@@ -333,7 +346,7 @@ those changes were made:\n";
     $header->{'Description'} .=
     $header->{'Description'} .=
 "\nThe person named in the Author field signed this changelog entry.\n";
 "\nThe person named in the Author field signed this changelog entry.\n";
     $header->{'Author'} = $ch_info->{'Maintainer'};
     $header->{'Author'} = $ch_info->{'Maintainer'};
-    my $text = "$header";
+    $text = "$header";
     run_vendor_hook("extend-patch-header", \$text, $ch_info);
     run_vendor_hook("extend-patch-header", \$text, $ch_info);
     $text .= "\n---
     $text .= "\n---
 The information above should follow the Patch Tagging Guidelines, please
 The information above should follow the Patch Tagging Guidelines, please