chiark / gitweb /
git-debrebase: Provide convert-from-unapplied aa alias for -gbp
[dgit.git] / dgit
diff --git a/dgit b/dgit
index 2f2b50918afe35c7a6a1ab33e632fcdd660fa37f..e10483894bb1084ae5693cf94c1347d959216c1a 100755 (executable)
--- a/dgit
+++ b/dgit
@@ -101,7 +101,11 @@ our %forceopts = map { $_=>0 }
 our %format_ok = map { $_=>1 } ("1.0","3.0 (native)","3.0 (quilt)");
 
 our $suite_re = '[-+.0-9a-z]+';
-our $cleanmode_re = 'dpkg-source(?:-d)?|git|git-ff|check|none';
+our $cleanmode_re = qr{(?: dpkg-source (?: -d )? (?: ,no-check | ,all-check )?
+                         | git | git-ff
+                         | check (?: ,ignores )?
+                         | none
+                         )}x;
 
 our $git_authline_re = '^([^<>]+) \<(\S+)\> (\d+ [-+]\d+)$';
 our $splitbraincache = 'dgit-intern/quilt-cache';
@@ -790,6 +794,9 @@ sub git_get_config ($) {
        @$l==1 or badcfg
            f_ "multiple values for %s (in %s git config)", $c, $src
            if @$l > 1;
+       $l->[0] =~ m/\n/ and badcfg f_
+ "value for config option %s (in %s git config) contains newline(s)!",
+            $c, $src;
        return $l->[0];
     }
     return undef;
@@ -2224,18 +2231,18 @@ sub generate_commits_from_dsc () {
 
        printdebug "considering saving $f: ";
 
-       if (link $f, $upper_f) {
+       if (rename_link_xf 1, $f, $upper_f) {
            printdebug "linked.\n";
-       } elsif ((printdebug "($!) "),
+       } elsif ((printdebug "($@) "),
                 $! != EEXIST) {
-           fail f_ "saving %s: %s", "$buildproductsdir/$f", $!;
+           fail f_ "saving %s: %s", "$buildproductsdir/$f", $@;
        } elsif (!$refetched) {
            printdebug "no need.\n";
-       } elsif (link $f, "$upper_f,fetch") {
+       } elsif (rename_link_xf 1, $f, "$upper_f,fetch") {
            printdebug "linked (using ...,fetch).\n";
-       } elsif ((printdebug "($!) "),
+       } elsif ((printdebug "($@) "),
                 $! != EEXIST) {
-           fail f_ "saving %s: %s", "$buildproductsdir/$f,fetch", $!;
+           fail f_ "saving %s: %s", "$buildproductsdir/$f,fetch", $@;
        } else {
            printdebug "cannot.\n";
        }
@@ -3817,12 +3824,25 @@ sub pull () {
 }
 
 sub check_not_dirty () {
-    foreach my $f (qw(local-options local-patch-header)) {
-       if (stat_exists "debian/source/$f") {
-           fail f_ "git tree contains debian/source/%s", $f;
+    my @forbid = qw(local-options local-patch-header);
+    @forbid = map { "debian/source/$_" } @forbid;
+    foreach my $f (@forbid) {
+       if (stat_exists $f) {
+           fail f_ "git tree contains %s", $f;
        }
     }
 
+    my @cmd = (@git, qw(status -uall --ignored --porcelain));
+    push @cmd, qw(debian/source/format debian/source/options);
+    push @cmd, @forbid;
+
+    my $bad = cmdoutput @cmd;
+    if (length $bad) {
+       fail +(__
+ "you have uncommitted changes to critical files, cannot continue:\n").
+              $bad;
+    }
+
     return if $includedirty;
 
     git_check_unmodified();
@@ -5503,7 +5523,8 @@ sub quiltify ($$$$) {
            printdebug "considering C=$c->{Commit} P=$p->{Commit}\n";
 
            my @cmd= (@git, qw(diff-tree -r --name-only),
-                     $p->{Commit},$c->{Commit}, qw(-- debian/patches .pc));
+                     $p->{Commit},$c->{Commit},
+                     qw(-- debian/patches .pc debian/source/format));
            my $patchstackchange = cmdoutput @cmd;
            if (length $patchstackchange) {
                $patchstackchange =~ s/\n/,/g;
@@ -5804,7 +5825,9 @@ sub quilt_fixup_singlepatch ($$$) {
     changedir "..";
     runcmd @dpkgsource, qw(-x), (srcfn $version, ".dsc");
     rename srcfn("$upstreamversion", "/debian/patches"), 
-           "work/debian/patches";
+       "work/debian/patches"
+       or $!==ENOENT
+       or confess "install d/patches: $!";
 
     changedir "work";
     commit_quilty_patch();
@@ -6180,34 +6203,71 @@ sub maybe_unapply_patches_again () {
 
 #----- other building -----
 
-our $clean_using_builder;
-# ^ tree is to be cleaned by dpkg-source's builtin idea that it should
-#   clean the tree before building (perhaps invoked indirectly by
-#   whatever we are using to run the build), rather than separately
-#   and explicitly by us.
+sub clean_tree_check_git ($$) {
+    my ($honour_ignores, $message) = @_;
+    my @cmd = (@git, qw(clean -dn));
+    push @cmd, qw(-x) unless $honour_ignores;
+    my $leftovers = cmdoutput @cmd;
+    if (length $leftovers) {
+       print STDERR $leftovers, "\n" or confess $!;
+       fail $message;
+    }
+}
+
+sub clean_tree_check_git_wd ($) {
+    my ($message) = @_;
+    return if $cleanmode =~ m{no-check};
+    return if $patches_applied_dirtily; # yuk
+    clean_tree_check_git +($cleanmode !~ m{all-check}),
+                         (f_ <<END, $message);
+%s
+If this is just missing .gitignore entries, use a different clean
+mode, eg --clean=dpkg-source,no-check (-wdu/-wddu) to ignore them
+or --clean=git (-wg/-wgf) to use \`git clean' instead.
+END
+}
+
+sub clean_tree_check () {
+    # Not yet fully implemented.
+    # This function needs to not care about modified but tracked files.
+    # That was done by check_not_dirty, and by now we may have run
+    # the rules clean target which might modify tracked files (!)
+    if ($cleanmode =~ m{^check}) {
+       clean_tree_check_git +($cleanmode =~ m{ignores}), __
+ "tree contains uncommitted files and --clean=check specified";
+    } elsif ($cleanmode =~ m{^dpkg-source}) {
+       clean_tree_check_git_wd __
+ "tree contains uncommitted files (NB dgit didn't run rules clean)";
+    } elsif ($cleanmode =~ m{^git}) {
+       # If we were actually cleaning these files would be summarily
+       # deleted.  Since we're not, and not using the working tree
+       # anyway, we can just ignore them - nothing will use them.
+    } elsif ($cleanmode eq 'none') {
+    } else {
+       confess "$cleanmode ?";
+    }
+}
 
 sub clean_tree () {
-    return if $clean_using_builder;
-    if ($cleanmode eq 'dpkg-source') {
+    # We always clean the tree ourselves, rather than leave it to the
+    # builder (dpkg-source, or soemthing which calls dpkg-source).
+    if ($cleanmode =~ m{^dpkg-source}) {
+       my @cmd = @dpkgbuildpackage;
+       push @cmd, qw(-d) if $cleanmode =~ m{^dpkg-source-d};
+       push @cmd, qw(-T clean);
        maybe_apply_patches_dirtily();
-       runcmd_ordryrun_local @dpkgbuildpackage, qw(-T clean);
-    } elsif ($cleanmode eq 'dpkg-source-d') {
-       maybe_apply_patches_dirtily();
-       runcmd_ordryrun_local @dpkgbuildpackage, qw(-d -T clean);
+       runcmd_ordryrun_local @cmd;
+       clean_tree_check_git_wd __
+ "tree contains uncommitted files (after running rules clean)";
     } elsif ($cleanmode eq 'git') {
        runcmd_ordryrun_local @git, qw(clean -xdf);
     } elsif ($cleanmode eq 'git-ff') {
        runcmd_ordryrun_local @git, qw(clean -xdff);
-    } elsif ($cleanmode eq 'check') {
-       my $leftovers = cmdoutput @git, qw(clean -xdn);
-       if (length $leftovers) {
-           print STDERR $leftovers, "\n" or confess $!;
-           fail __
- "tree contains uncommitted files and --clean=check specified";
-       }
+    } elsif ($cleanmode =~ m{^check}) {
+       clean_tree_check();
     } elsif ($cleanmode eq 'none') {
     } else {
-       die "$cleanmode ?";
+       confess "$cleanmode ?";
     }
 }
 
@@ -6243,9 +6303,18 @@ sub build_prep_early () {
 sub build_prep ($) {
     my ($wantsrc) = @_;
     build_prep_early();
-    # clean the tree if we're trying to include dirty changes in the
-    # source package, or we are running the builder in $maindir
-    clean_tree() if $includedirty || ($wantsrc & WANTSRC_BUILDER);
+    if (!building_source_in_playtree() || ($wantsrc & WANTSRC_BUILDER)) {
+       # Clean the tree because we're going to use the contents of
+       # $maindir.  (We trying to include dirty changes in the source
+       # package, or we are running the builder in $maindir.)
+       clean_tree();
+    } else {
+       # We don't actually need to do anything in $maindir, but we
+       # should do some kind of cleanliness check because (i) the
+       # user may have forgotten a `git add', and (ii) if the user
+       # said -wc we should still do the check.
+       clean_tree_check();
+    }
     build_maybe_quilt_fixup();
     if ($rmchanges) {
        my $pat = changespat $version;
@@ -6516,9 +6585,7 @@ sub cmd_gbp_build {
        build_source();
        midbuild_checkchanges_vanilla $wantsrc;
     } else {
-       if (!$clean_using_builder) {
-           push @cmd, '--git-cleaner=true';
-       }
+       push @cmd, '--git-cleaner=true';
     }
     maybe_unapply_patches_again();
     if ($wantsrc & WANTSRC_BUILDER) {
@@ -6566,6 +6633,24 @@ sub build_source {
         }
     } else {
         $leafdir = basename $maindir;
+
+       if ($buildproductsdir ne '..') {
+           # Well, we are going to run dpkg-source -b which consumes
+           # origs from .. and generates output there.  To make this
+           # work when the bpd is not .. , we would have to (i) link
+           # origs from bpd to .. , (ii) check for files that
+           # dpkg-source -b would/might overwrite, and afterwards
+           # (iii) move all the outputs back to the bpd (iv) except
+           # for the origs which should be deleted from .. if they
+           # weren't there beforehand.  And if there is an error and
+           # we don't run to completion we would necessarily leave a
+           # mess.  This is too much.  The real way to fix this
+           # is for dpkg-source to have bpd support.
+           confess unless $includedirty;
+           fail __
+ "--include-dirty not supported with --build-products-dir, sorry";
+       }
+
         changedir '..';
     }
     runcmd_ordryrun_local @cmd, $leafdir;
@@ -6582,8 +6667,8 @@ sub build_source {
     my $mv = sub {
        my ($why, $l) = @_;
         printdebug " renaming ($why) $l\n";
-        rename "$l", bpd_abs()."/$l"
-           or fail f_ "put in place new built file (%s): %s", $l, $!;
+        rename_link_xf 0, "$l", bpd_abs()."/$l"
+           or fail f_ "put in place new built file (%s): %s", $l, $@;
     };
     foreach my $l (split /\n/, getfield $dsc, 'Files') {
         $l =~ m/\S+$/ or next;
@@ -7202,15 +7287,18 @@ sub parseopts () {
                } elsif (s/^-wgf$//s) {
                    push @ropts, $&;
                    $cleanmode = 'git-ff';
-               } elsif (s/^-wd$//s) {
+               } elsif (s/^-wd(d?)([na]?)$//s) {
                    push @ropts, $&;
                    $cleanmode = 'dpkg-source';
-               } elsif (s/^-wdd$//s) {
-                   push @ropts, $&;
-                   $cleanmode = 'dpkg-source-d';
+                   $cleanmode .= '-d' if $1;
+                   $cleanmode .= ',no-check' if $2 eq 'n';
+                   $cleanmode .= ',all-check' if $2 eq 'a';
                } elsif (s/^-wc$//s) {
                    push @ropts, $&;
                    $cleanmode = 'check';
+               } elsif (s/^-wci$//s) {
+                   push @ropts, $&;
+                   $cleanmode = 'check,ignores';
                } elsif (s/^-c([^=]*)\=(.*)$//s) {
                    push @git, '-c', $&;
                    $gitcfgs{cmdline}{$1} = [ $2 ];
@@ -7318,11 +7406,14 @@ sub parseopts_late_defaults () {
 
     if (!defined $cleanmode) {
        local $access_forpush;
-       $cleanmode = access_cfg('clean-mode', 'RETURN-UNDEF');
+       $cleanmode = access_cfg('clean-mode-newer', 'RETURN-UNDEF');
+       $cleanmode = undef if $cleanmode && $cleanmode !~ m/^$cleanmode_re$/;
+
+       $cleanmode //= access_cfg('clean-mode', 'RETURN-UNDEF');
        $cleanmode //= 'dpkg-source';
 
        badcfg f_ "unknown clean-mode \`%s'", $cleanmode unless
-           $cleanmode =~ m/^($cleanmode_re)$(?!\n)/s;
+           $cleanmode =~ m/$cleanmode_re/;
     }
 
     $buildproductsdir //= access_cfg('build-products-dir', 'RETURN-UNDEF');
@@ -7355,7 +7446,10 @@ $cmd =~ y/-/_/;
 my $pre_fn = ${*::}{"pre_$cmd"};
 $pre_fn->() if $pre_fn;
 
-record_maindir if $invoked_in_git_tree;
+if ($invoked_in_git_tree) {
+    changedir_git_toplevel();
+    record_maindir();
+}
 git_slurp_config();
 
 my $fn = ${*::}{"cmd_$cmd"};