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
# $@ 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
$@ = "$!";
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 ($) {
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)