chiark / gitweb /
Replace `confess $!' with `confess "$!"', to actually print errno
[dgit.git] / Debian / Dgit.pm
index edc57f12c579f636224d0181efaf2b45d0c59790..61476d9f8e18b8adc6dacb7a8926e68c604fc58b 100644 (file)
@@ -48,13 +48,13 @@ BEGIN {
                      upstreamversion
                      stripepoch source_file_leafname is_orig_file_of_p_v
                      server_branch server_ref
-                      stat_exists link_ltarget
+                      stat_exists link_ltarget rename_link_xf
                      hashfile
                       fail failmsg ensuredir must_getcwd executable_on_path
                       waitstatusmsg failedcmd_waitstatus
                      failedcmd_report_cmd failedcmd
                       runcmd shell_cmd cmdoutput cmdoutput_errok
-                      git_rev_parse git_cat_file
+                      git_rev_parse changedir_git_toplevel git_cat_file
                      git_get_ref git_get_symref git_for_each_ref
                       git_for_each_tag_referring is_fast_fwd
                      git_check_unmodified
@@ -98,7 +98,7 @@ our $distro_re = $component_re;
 our $versiontag_re = qr{[-+.\%_0-9a-zA-Z/]+};
 our $branchprefix = 'dgit';
 our $series_filename_re = qr{(?:^|\.)series(?!\n)$}s;
-our $extra_orig_namepart_re = qr{[-0-9a-z]+};
+our $extra_orig_namepart_re = qr{[-0-9a-zA-Z]+};
 our $orig_f_comp_re = qr{orig(?:-$extra_orig_namepart_re)?};
 our $orig_f_sig_re = '\\.(?:asc|gpg|pgp)';
 our $orig_f_tail_re = "$orig_f_comp_re\\.tar(?:\\.\\w+)?(?:$orig_f_sig_re)?";
@@ -148,11 +148,11 @@ sub setup_sigwarn () {
 
 sub initdebug ($) { 
     ($debugprefix) = @_;
-    open DEBUG, ">/dev/null" or confess $!;
+    open DEBUG, ">/dev/null" or confess "$!";
 }
 
 sub enabledebug () {
-    open DEBUG, ">&STDERR" or confess $!;
+    open DEBUG, ">&STDERR" or confess "$!";
     DEBUG->autoflush(1);
     $debuglevel ||= 1;
 }
@@ -181,7 +181,7 @@ sub printdebug {
     print DEBUG $debugprefix unless $printdebug_noprefix;
     pop @_ while @_ and !length $_[-1];
     return unless @_;
-    print DEBUG @_ or confess $!;
+    print DEBUG @_ or confess "$!";
     $printdebug_noprefix = $_[-1] !~ m{\n$};
 }
 
@@ -214,9 +214,9 @@ sub shellquote {
 sub printcmd {
     my $fh = shift @_;
     my $intro = shift @_;
-    print $fh $intro," " or confess $!;
-    print $fh shellquote @_ or confess $!;
-    print $fh "\n" or confess $!;
+    print $fh $intro," " or confess "$!";
+    print $fh shellquote @_ or confess "$!";
+    print $fh "\n" or confess "$!";
 }
 
 sub debugcmd {
@@ -347,7 +347,7 @@ sub waitstatusmsg () {
 sub failedcmd_report_cmd {
     my $intro = shift @_;
     $intro //= __ "failed command";
-    { local ($!); printcmd \*STDERR, _us().": $intro:", @_ or confess $!; };
+    { local ($!); printcmd \*STDERR, _us().": $intro:", @_ or confess "$!"; };
 }
 
 sub failedcmd_waitstatus {
@@ -395,7 +395,7 @@ sub cmdoutput_errok {
     my $d;
     $!=0; $?=0;
     { local $/ = undef; $d = <P>; }
-    confess $! if P->error;
+    confess "$!" if P->error;
     if (!close P) { printdebug "=>!$?\n"; return undef; }
     chomp $d;
     if ($debuglevel > 0) {
@@ -425,6 +425,70 @@ sub link_ltarget ($$) {
     $r or fail "(sym)link $old $new: $!\n";
 }
 
+sub rename_link_xf ($$$) {
+    # renames/moves or links/copies $src to $dst,
+    # even if $dst is on a different fs
+    # (May use the filename "$dst.tmp".);
+    # On success, returns true.
+    # On failure, returns false and sets
+    #    $@ to a reason message
+    #    $! to an errno value, or -1 if not known
+    # having possibly printed something about mv to stderr.
+    # Not safe to use without $keeporig if $dst might be a symlink
+    # to $src, as it might delete $src leaving $dst invalid.
+    my ($keeporig,$src,$dst) = @_;
+    if ($keeporig
+       ? link   $src, $dst
+       : rename $src, $dst) {
+       return 1;
+    }
+    if ($! != EXDEV) {
+       $@ = "$!";
+       return 0;
+    }
+    if (!stat $src) {
+       $@ = f_ "stat source file: %S", $!;
+       return 0;
+    }
+    my @src_stat = (stat _)[0..1];
+
+    my @dst_stat;
+    if (stat $dst) {
+       @dst_stat = (stat _)[0..1];
+    } elsif ($! == ENOENT) {
+    } else {
+       $@ = f_ "stat destination file: %S", $!;
+       return 0;
+    }
+
+    if ("@src_stat" eq "@dst_stat") {
+       # (Symlinks to) the same file.  No need for a copy but
+       # we may need to delete the original.
+       printdebug "rename_link_xf $keeporig $src $dst EXDEV but same\n";
+    } else {
+       $!=0; $?=0;
+       my @cmd = (qw(cp --), $src, "$dst.tmp");
+       debugcmd '+',@cmd;
+       if (system @cmd) {
+           failedcmd_report_cmd undef, @cmd;
+           $@ = failedcmd_waitstatus();
+           $! = -1;
+           return 0;
+       }
+       if (!rename "$dst.tmp", $dst) {
+           $@ = f_ "finally install file after cp: %S", $!;
+           return 0;
+       }
+    }
+    if (!$keeporig) {
+       if (!unlink $src) {
+           $@ = f_ "delete old file after cp: %S", $!;
+           return 0;
+       }
+    }
+    return 1;
+}
+
 sub hashfile ($) {
     my ($fn) = @_;
     my $h = Digest::SHA->new(256);
@@ -436,6 +500,15 @@ sub git_rev_parse ($) {
     return cmdoutput qw(git rev-parse), "$_[0]~0";
 }
 
+sub changedir_git_toplevel () {
+    my $toplevel = cmdoutput qw(git rev-parse --show-toplevel);
+    length $toplevel or fail __ <<END;
+not in a git working tree?
+(git rev-parse --show-toplevel produced no output)
+END
+    chdir $toplevel or fail f_ "chdir toplevel %s: %s\n", $toplevel, $!;
+}
+
 sub git_cat_file ($;$) {
     my ($objname, $etype) = @_;
     # => ($type, $data) or ('missing', undef)
@@ -455,10 +528,10 @@ sub git_cat_file ($;$) {
     if (!$gcf_pid) {
        my @cmd = qw(git cat-file --batch);
        debugcmd "GCF|", @cmd;
-       $gcf_pid = open2 $gcf_o, $gcf_i, @cmd or confess $!;
+       $gcf_pid = open2 $gcf_o, $gcf_i, @cmd or confess "$!";
     }
     printdebug "GCF>| $objname\n";
-    print $gcf_i $objname, "\n" or confess $!;
+    print $gcf_i $objname, "\n" or confess "$!";
     my $x = <$gcf_o>;
     printdebug "GCF<| ", $x;
     if ($x =~ m/ (missing)$/) { return $chk->($1, undef); }
@@ -494,7 +567,7 @@ sub git_for_each_ref ($$;$) {
     if (defined $gitdir) {
        @cmd = ('sh','-ec','cd "$1"; shift; exec "$@"','x', $gitdir, @cmd);
     }
-    open GFER, "-|", @cmd or confess $!;
+    open GFER, "-|", @cmd or confess "$!";
     debugcmd "|", @cmd;
     while (<GFER>) {
        chomp or confess "$_ ?";
@@ -592,7 +665,7 @@ sub git_slurp_config_src ($) {
     local $/="\0";
 
     my $r = { };
-    open GITS, "-|", @cmd or confess $!;
+    open GITS, "-|", @cmd or confess "$!";
     while (<GITS>) {
        chomp or confess;
        printdebug "=> ", (messagequote $_), "\n";
@@ -655,7 +728,7 @@ sub parsecontrol {
     my $fh = new IO::Handle;
     open $fh, '<', $file or fail f_ "open %s (%s): %s", $file, $desc, $!;
     my $c = parsecontrolfh($fh,$desc,$allowsigned);
-    $fh->error and confess $!;
+    $fh->error and confess "$!";
     close $fh;
     return $c;
 }
@@ -664,7 +737,7 @@ sub parsechangelog {
     my $c = Dpkg::Control::Hash->new(name => 'parsed changelog');
     my $p = new IO::Handle;
     my @cmd = (qw(dpkg-parsechangelog), @_);
-    open $p, '-|', @cmd or confess $!;
+    open $p, '-|', @cmd or confess "$!";
     $c->parse($p);
     $?=0; $!=0; close $p or failedcmd @cmd;
     return $c;
@@ -682,7 +755,7 @@ sub parsechangelog_loop ($$$) {
     # @$clogcmd is qw(dpkg-parsechangelog ...some...options...)
     # calls $fn->($thisstanza, $desc);
     debugcmd "|",@$clogcmd;
-    open CLOGS, "-|", @$clogcmd or confess $!;
+    open CLOGS, "-|", @$clogcmd or confess "$!";
     for (;;) {
        my $stanzatext = do { local $/=""; <CLOGS>; };
        printdebug "clogp stanza ".Dumper($stanzatext) if $debuglevel>1;
@@ -694,7 +767,7 @@ sub parsechangelog_loop ($$$) {
 
        $fn->($thisstanza, $desc);
     }
-    confess $! if CLOGS->error;
+    confess "$!" if CLOGS->error;
     close CLOGS or $?==SIGPIPE or failedcmd @$clogcmd;
 }      
 
@@ -704,11 +777,11 @@ sub make_commit_text ($) {
     my @cmd = (qw(git hash-object -w -t commit --stdin));
     debugcmd "|",@cmd;
     print Dumper($text) if $debuglevel > 1;
-    my $child = open2($out, $in, @cmd) or confess $!;
+    my $child = open2($out, $in, @cmd) or confess "$!";
     my $h;
     eval {
-       print $in $text or confess $!;
-       close $in or confess $!;
+       print $in $text or confess "$!";
+       close $in or confess "$!";
        $h = <$out>;
        $h =~ m/^\w+$/ or confess;
        $h = $&;
@@ -730,7 +803,7 @@ sub reflog_cache_insert ($$$) {
     my $parent = $ref; $parent =~ s{/[^/]+$}{};
     ensuredir "$maindir_gitcommon/logs/$parent";
     my $makelogfh = new IO::File "$maindir_gitcommon/logs/$ref", '>>'
-      or confess $!;
+      or confess "$!";
 
     my $oldcache = git_get_ref $ref;
 
@@ -759,11 +832,11 @@ sub reflog_cache_lookup ($$) {
     # you must have called record_maindir
     my @cmd = (qw(git log -g), '--pretty=format:%H %gs', $ref);
     debugcmd "|(probably)",@cmd;
-    my $child = open GC, "-|";  defined $child or confess $!;
+    my $child = open GC, "-|";  defined $child or confess "$!";
     if (!$child) {
-       chdir $maindir or confess $!;
+       chdir $maindir or confess "$!";
        if (!stat "$maindir_gitcommon/logs/$ref") {
-           $! == ENOENT or confess $!;
+           $! == ENOENT or confess "$!";
            printdebug ">(no reflog)\n";
            finish 0;
        }
@@ -776,7 +849,7 @@ sub reflog_cache_lookup ($$) {
        close GC;
        return $1;
     }
-    confess $! if GC->error;
+    confess "$!" if GC->error;
     failedcmd unless close GC;
     return undef;
 }
@@ -902,11 +975,11 @@ sub playtree_setup (;$) {
     #   $maindir_gitdir     contains our main working "dgit", HEAD, etc.
     #   $maindir_gitcommon  the shared stuff, including .objects
     rmtree('.git/objects');
-    symlink "$maindir_gitcommon/objects",'.git/objects' or confess $!;
+    symlink "$maindir_gitcommon/objects",'.git/objects' or confess "$!";
     ensuredir '.git/info';
-    open GA, "> .git/info/attributes" or confess $!;
-    print GA "* $negate_harmful_gitattrs\n" or confess $!;
-    close GA or confess $!;
+    open GA, "> .git/info/attributes" or confess "$!";
+    print GA "* $negate_harmful_gitattrs\n" or confess "$!";
+    close GA or confess "$!";
 }
 
 1;