X-Git-Url: https://git.octo.it/?p=git.git;a=blobdiff_plain;f=git-mv.perl;h=75aa8feeb6201c54e55bfaca0cc178c53891e776;hp=a21d87eea88d4b1e9a40e4e7f7b836374f566c70;hb=HEAD;hpb=235d521da60e4699e5bd59ac658b5b48bd76ddca diff --git a/git-mv.perl b/git-mv.perl index a21d87ee..75aa8fee 100755 --- a/git-mv.perl +++ b/git-mv.perl @@ -13,51 +13,32 @@ use Getopt::Std; sub usage() { print < -$0 [-f] [-k] [-n] ... - -In the first form, source must exist and be either a file, -symlink or directory, dest must not exist. It renames source to dest. -In the second form, the last argument has to be an existing -directory; the given sources will be moved into this directory. - -Updates the git cache to reflect the change. -Use "git commit" to make the change permanently. - -Options: - -f Force renaming/moving, even if target exists - -k Continue on error by skipping - not-existing or not revision-controlled source - -n Do nothing; show what would happen +$0 [-f] [-n] +$0 [-f] [-n] [-k] ... EOT exit(1); } -# Sanity checks: -my $GIT_DIR = $ENV{'GIT_DIR'} || ".git"; - -unless ( -d $GIT_DIR && -d $GIT_DIR . "/objects" && - -d $GIT_DIR . "/objects/" && -d $GIT_DIR . "/refs") { - print "Git repository not found."; - usage(); -} - - our ($opt_n, $opt_f, $opt_h, $opt_k, $opt_v); getopts("hnfkv") || usage; usage() if $opt_h; @ARGV >= 1 or usage; +my $GIT_DIR = `git rev-parse --git-dir`; +exit 1 if $?; # rev-parse would have given "not a git dir" message. +chomp($GIT_DIR); + my (@srcArgs, @dstArgs, @srcs, @dsts); my ($src, $dst, $base, $dstDir); +# remove any trailing slash in arguments +for (@ARGV) { s/\/*$//; } + my $argCount = scalar @ARGV; if (-d $ARGV[$argCount-1]) { $dstDir = $ARGV[$argCount-1]; - # remove any trailing slash - $dstDir =~ s/\/$//; @srcArgs = @ARGV[0..$argCount-2]; - + foreach $src (@srcArgs) { $base = $src; $base =~ s/^.*\///; @@ -66,23 +47,45 @@ if (-d $ARGV[$argCount-1]) { } } else { - if ($argCount != 2) { + if ($argCount < 2) { + print "Error: need at least two arguments\n"; + exit(1); + } + if ($argCount > 2) { print "Error: moving to directory '" . $ARGV[$argCount-1] - . "' not possible; not exisiting\n"; - usage; + . "' not possible; not existing\n"; + exit(1); } @srcArgs = ($ARGV[0]); @dstArgs = ($ARGV[1]); $dstDir = ""; } +my $subdir_prefix = `git rev-parse --show-prefix`; +chomp($subdir_prefix); + +# run in git base directory, so that git-ls-files lists all revisioned files +chdir "$GIT_DIR/.."; + +# normalize paths, needed to compare against versioned files and update-index +# also, this is nicer to end-users by doing ".//a/./b/.//./c" ==> "a/b/c" +for (@srcArgs, @dstArgs) { + # prepend git prefix as we run from base directory + $_ = $subdir_prefix.$_; + s|^\./||; + s|/\./|/| while (m|/\./|); + s|//+|/|g; + # Also "a/b/../c" ==> "a/c" + 1 while (s,(^|/)[^/]+/\.\./,$1,); +} + my (@allfiles,@srcfiles,@dstfiles); my $safesrc; my (%overwritten, %srcForDst); $/ = "\0"; -open(F,"-|","git-ls-files","-z") +open(F, 'git-ls-files -z |') or die "Failed to open pipe from git-ls-files: " . $!; @allfiles = map { chomp; $_; } ; @@ -95,6 +98,15 @@ while(scalar @srcArgs > 0) { $dst = shift @dstArgs; $bad = ""; + for ($src, $dst) { + # Be nicer to end-users by doing ".//a/./b/.//./c" ==> "a/b/c" + s|^\./||; + s|/\./|/| while (m|/\./|); + s|//+|/|g; + # Also "a/b/../c" ==> "a/c" + 1 while (s,(^|/)[^/]+/\.\./,$1,); + } + if ($opt_v) { print "Checking rename of '$src' to '$dst'\n"; } @@ -103,23 +115,30 @@ while(scalar @srcArgs > 0) { $bad = "bad source '$src'"; } + $safesrc = quotemeta($src); + @srcfiles = grep /^$safesrc(\/|$)/, @allfiles; + $overwritten{$dst} = 0; if (($bad eq "") && -e $dst) { $bad = "destination '$dst' already exists"; - if (-f $dst && $opt_f) { - print "Warning: $bad; will overwrite!\n"; - $bad = ""; - $overwritten{$dst} = 1; + if ($opt_f) { + # only files can overwrite each other: check both source and destination + if (-f $dst && (scalar @srcfiles == 1)) { + print "Warning: $bad; will overwrite!\n"; + $bad = ""; + $overwritten{$dst} = 1; + } + else { + $bad = "Can not overwrite '$src' with '$dst'"; + } } } - if (($bad eq "") && ($src eq $dstDir)) { + if (($bad eq "") && ($dst =~ /^$safesrc\//)) { $bad = "can not move directory '$src' into itself"; } if ($bad eq "") { - $safesrc = quotemeta($src); - @srcfiles = grep /^$safesrc(\/|$)/, @allfiles; if (scalar @srcfiles == 0) { $bad = "'$src' not under version control"; } @@ -141,7 +160,7 @@ while(scalar @srcArgs > 0) { next; } print "Error: $bad\n"; - usage(); + exit(1); } push @srcs, $src; push @dsts, $dst; @@ -149,14 +168,22 @@ while(scalar @srcArgs > 0) { # Final pass: rename/move my (@deletedfiles,@addedfiles,@changedfiles); +$bad = ""; while(scalar @srcs > 0) { $src = shift @srcs; $dst = shift @dsts; if ($opt_n || $opt_v) { print "Renaming $src to $dst\n"; } if (!$opt_n) { - rename($src,$dst) - or die "rename failed: $!"; + if (!rename($src,$dst)) { + $bad = "renaming '$src' failed: $!"; + if ($opt_k) { + print "Warning: skipped: $bad\n"; + $bad = ""; + next; + } + last; + } } $safesrc = quotemeta($src); @@ -166,10 +193,12 @@ while(scalar @srcs > 0) { push @deletedfiles, @srcfiles; if (scalar @srcfiles == 1) { + # $dst can be a directory with 1 file inside if ($overwritten{$dst} ==1) { - push @changedfiles, $dst; + push @changedfiles, $dstfiles[0]; + } else { - push @addedfiles, $dst; + push @addedfiles, $dstfiles[0]; } } else { @@ -178,20 +207,44 @@ while(scalar @srcs > 0) { } if ($opt_n) { + if (@changedfiles) { print "Changed : ". join(", ", @changedfiles) ."\n"; + } + if (@addedfiles) { print "Adding : ". join(", ", @addedfiles) ."\n"; + } + if (@deletedfiles) { print "Deleting : ". join(", ", @deletedfiles) ."\n"; - exit(1); + } } - -my $rc; -if (scalar @changedfiles >0) { - $rc = system("git-update-index","--",@changedfiles); - die "git-update-index failed to update changed files with code $?\n" if $rc; +else { + if (@changedfiles) { + open(H, "| git-update-index -z --stdin") + or die "git-update-index failed to update changed files with code $!\n"; + foreach my $fileName (@changedfiles) { + print H "$fileName\0"; + } + close(H); + } + if (@addedfiles) { + open(H, "| git-update-index --add -z --stdin") + or die "git-update-index failed to add new names with code $!\n"; + foreach my $fileName (@addedfiles) { + print H "$fileName\0"; + } + close(H); + } + if (@deletedfiles) { + open(H, "| git-update-index --remove -z --stdin") + or die "git-update-index failed to remove old names with code $!\n"; + foreach my $fileName (@deletedfiles) { + print H "$fileName\0"; + } + close(H); + } } -if (scalar @addedfiles >0) { - $rc = system("git-update-index","--add","--",@addedfiles); - die "git-update-index failed to add new names with code $?\n" if $rc; + +if ($bad ne "") { + print "Error: $bad\n"; + exit(1); } -$rc = system("git-update-index","--remove","--",@deletedfiles); -die "git-update-index failed to remove old names with code $?\n" if $rc;