chiark / gitweb /
changelog: start 8.5~
[dgit.git] / Debian / Dgit.pm
index b8a1b8c969ffe47d8a5b7b12153bf14871684747..2ef32f32a8179f9dc590bfdbcf27419470a62f21 100644 (file)
@@ -54,7 +54,7 @@ BEGIN {
                       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)?";
@@ -434,31 +434,59 @@ sub rename_link_xf ($$$) {
     #    $@ 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;
-    } elsif ($! != EXDEV) {
+    }
+    if ($! != EXDEV) {
        $@ = "$!";
        return 0;
     }
-    $!=0; $?=0;
-    my @cmd = ($keeporig ? qw(cp) : qw(mv));
-    push @cmd, (qw(--), $src, "$dst.tmp");
-    debugcmd '+',@cmd;
-    if (system @cmd) {
-       failedcmd_report_cmd undef, @cmd;
-       $@ = failedcmd_waitstatus();
-       $! = -1;
+    if (!stat $src) {
+       $@ = f_ "stat source file: %S", $!;
        return 0;
     }
-    if (rename "$dst.tmp", $dst) {
-       return 1;
+    my @src_stat = (stat _)[0..1];
+
+    my @dst_stat;
+    if (stat $dst) {
+       @dst_stat = (stat _)[0..1];
+    } elsif ($! == ENOENT) {
     } else {
-       $@ = f_ "finally install file after mv: %S", $!;
+       $@ = 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 ($) {
@@ -472,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)