combine-diff: better hunk splitting.
[gitweb.git] / git-mv.perl
index 17e35b0ac088f83bbcd6d104c77925cd69ded2bb..83dc7e45cf205c7575512182bf29516914773209 100755 (executable)
 
 sub usage() {
        print <<EOT;
-$0 [-f] [-n] <source> <dest>
-$0 [-f] [-k] [-n] <source> ... <dest directory>
-
-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] <source> <destination>
+$0 [-f] [-n] [-k] <source> ... <destination directory>
 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();
-}
-
+my $GIT_DIR = `git rev-parse --git-dir`;
+exit 1 if $?; # rev-parse would have given "not a git dir" message.
+chomp($GIT_DIR);
 
 our ($opt_n, $opt_f, $opt_h, $opt_k, $opt_v);
 getopts("hnfkv") || usage;
@@ -54,6 +34,8 @@ ()
 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) {
@@ -68,7 +50,7 @@ ()
        print "Error: moving to directory '"
            . $ARGV[$argCount-1]
            . "' not possible; not exisiting\n";
-       usage;
+       exit(1);
     }
     @srcArgs = ($ARGV[0]);
     @dstArgs = ($ARGV[1]);
@@ -80,7 +62,7 @@ ()
 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; $_; } <F>;
@@ -101,23 +83,30 @@ ()
        $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";
        }
@@ -139,7 +128,7 @@ ()
            next;
        }
        print "Error: $bad\n";
-       usage();
+       exit(1);
     }
     push @srcs, $src;
     push @dsts, $dst;
@@ -147,14 +136,22 @@ ()
 
 # 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);
@@ -164,10 +161,12 @@ ()
 
     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 {
@@ -176,20 +175,44 @@ ()
 }
 
 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;