Explorar el Código

Merge branch 'sid'

Raphaël Hertzog hace 16 años
padre
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
 
+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
 
   [ Raphael Hertzog ]

+ 37 - 16
man/dpkg-source.1

@@ -394,7 +394,7 @@ such options, and the build is likely to fail.
 .PP
 Similarly to quilt's default behaviour, the patches can remove files too.
 .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.
 .PP
 .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
 temporary directory by following the same logic as for the unpack, the
 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
 failure unless the maintainer deliberately decided to include that
 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
 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
-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
 behaviour.
 .PP
 .B Build options
 .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
 Do not ignore removed files and include them in the automatically
 generated patch.
@@ -454,6 +464,17 @@ in subsequent builds and this option is thus no more needed.
 .B \-\-no\-preparation
 Do not try to prepare the build tree by applying patches which are
 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
 .B Extract options
 .TP
@@ -462,11 +483,6 @@ Skips extraction of the debian tarball on top of the upstream sources.
 .TP
 .B \-\-skip\-patches
 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)
 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
   compression = "bzip2"
   compression-level = 9
+  # use debian/patches/debian-changes as automatic patch
+  single-debian-patch
 .P
 Note: \fBformat\fR options are not accepted in this file, you should
 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
 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

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

@@ -128,7 +128,7 @@ sub parse {
 	    $entry->set_part("trailer", $_);
 	    $entry->extend_part("blank_after_changes", [ @blanklines ]);
 	    @blanklines = ();
-	    foreach my $error ($entry->check_header()) {
+	    foreach my $error ($entry->check_trailer()) {
 		$self->parse_error($file, $., $error, $_);
 	    }
 	    $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 {
     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)
@@ -285,7 +285,7 @@ Returns and removes all the items that matches all the criteria.
 
 sub remove {
     my ($self, %crit) = @_;
-    my @keys = $self->get_by_keys(%crit);
+    my @keys = $self->get_keys(%crit);
     my (%keys, @ret);
     foreach my $key (@keys) {
 	$keys{$key} = 1;

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

@@ -151,7 +151,8 @@ sub do_extract {
                   @exclude_symlinks ]);
 
     # 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 {
@@ -159,7 +160,8 @@ sub get_autopatch_name {
 }
 
 sub get_patches {
-    my ($self, $dir, $skip_auto) = @_;
+    my ($self, $dir, %opts) = @_;
+    $opts{"skip_auto"} = 0 unless defined($opts{"skip_auto"});
     my @patches;
     my $pd = "$dir/debian/patches";
     my $auto_patch = $self->get_autopatch_name();
@@ -168,7 +170,7 @@ sub get_patches {
         foreach my $patch (sort readdir(DIR)) {
             # patches match same rules as run-parts
             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;
         }
         closedir(DIR);
@@ -177,15 +179,16 @@ sub get_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);
     my $timestamp = time();
     my $applied = File::Spec->catfile($dir, "debian", "patches", ".dpkg-source-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);
-        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);
         $patch_obj->apply($dir, force_timestamp => 1,
                           timestamp => $timestamp,
@@ -220,7 +223,7 @@ sub check_patches_applied {
     my $applied = File::Spec->catfile($dir, "debian", "patches", ".dpkg-source-applied");
     unless (-e $applied) {
         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 $?;
 
     # 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
     my %auth_bin_files;
@@ -428,6 +431,14 @@ sub do_build {
 
 sub get_patch_header {
     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
  This patch has been created by dpkg-source during the package build
  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
 # it under the terms of the GNU General Public License as published by
@@ -25,6 +25,7 @@ use Dpkg;
 use Dpkg::Gettext;
 use Dpkg::ErrorHandling;
 use Dpkg::Source::Patch;
+use Dpkg::Source::Functions qw(erasedir);
 use Dpkg::IPC;
 use Dpkg::Vendor qw(get_current_vendor run_vendor_hook);
 use Dpkg::Control;
@@ -39,17 +40,22 @@ our $CURRENT_MINOR_VERSION = "0";
 
 sub init_options {
     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();
-    # 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 {
     my ($self, $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 0;
@@ -75,7 +81,11 @@ sub can_build {
 
 sub get_autopatch_name {
     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 {
@@ -89,63 +99,80 @@ sub get_series_file {
     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 $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;
 }
 
-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 {
-    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
     my $series = $self->get_series_file($dir);
@@ -159,45 +186,22 @@ sub apply_patches {
                 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);
 }
@@ -210,7 +214,6 @@ sub prepare_build {
     # stamp file created by ourselves
     my $func = sub {
         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] =~ /$self->{'options'}{'diff_ignore_regexp'}/;
         return 0;
@@ -218,99 +221,99 @@ sub prepare_build {
     $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 {
     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);
         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 {
     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 @patches = $self->get_patches($dir);
     my $has_patch = (grep { $_ eq $auto_patch } @patches) ? 1 : 0;
     my $series = $self->get_series_file($dir);
     $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 $absdir = $dir;
-    unless (File::Spec->file_name_is_absolute($absdir)) {
-        $absdir = File::Spec->rel2abs($dir);
-    }
+
     if (-e $patch) {
         # Add auto_patch to series file
         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 {
         # Remove auto_patch from series
         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
         unlink($series) if not -s $series;
@@ -319,9 +322,19 @@ sub register_autopatch {
 
 sub get_patch_header {
     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,
         file => File::Spec->catfile($dir, "debian", "changelog"));
     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);
     $header->{'Description'} = "Upstream changes introduced in version " .
                                $ch_info->{'Version'} . "\n";
@@ -333,7 +346,7 @@ those changes were made:\n";
     $header->{'Description'} .=
 "\nThe person named in the Author field signed this changelog entry.\n";
     $header->{'Author'} = $ch_info->{'Maintainer'};
-    my $text = "$header";
+    $text = "$header";
     run_vendor_hook("extend-patch-header", \$text, $ch_info);
     $text .= "\n---
 The information above should follow the Patch Tagging Guidelines, please