git-mv.perlon commit git-upload-pack: More efficient usage of the has_sha1 array (794f9fe)
   1#!/usr/bin/perl
   2#
   3# Copyright 2005, Ryan Anderson <ryan@michonline.com>
   4#                 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
   5#
   6# This file is licensed under the GPL v2, or a later version
   7# at the discretion of Linus Torvalds.
   8
   9
  10use warnings;
  11use strict;
  12use Getopt::Std;
  13
  14sub usage() {
  15        print <<EOT;
  16$0 [-f] [-n] <source> <dest>
  17$0 [-f] [-k] [-n] <source> ... <dest directory>
  18
  19In the first form, source must exist and be either a file,
  20symlink or directory, dest must not exist. It renames source to dest.
  21In the second form, the last argument has to be an existing
  22directory; the given sources will be moved into this directory.
  23
  24Updates the git cache to reflect the change.
  25Use "git commit" to make the change permanently.
  26
  27Options:
  28  -f   Force renaming/moving, even if target exists
  29  -k   Continue on error by skipping
  30       not-existing or not revision-controlled source
  31  -n   Do nothing; show what would happen
  32EOT
  33        exit(1);
  34}
  35
  36# Sanity checks:
  37my $GIT_DIR = $ENV{'GIT_DIR'} || ".git";
  38
  39unless ( -d $GIT_DIR && -d $GIT_DIR . "/objects" && 
  40        -d $GIT_DIR . "/objects/" && -d $GIT_DIR . "/refs") {
  41    print "Git repository not found.";
  42    usage();
  43}
  44
  45
  46our ($opt_n, $opt_f, $opt_h, $opt_k, $opt_v);
  47getopts("hnfkv") || usage;
  48usage() if $opt_h;
  49@ARGV >= 1 or usage;
  50
  51my (@srcArgs, @dstArgs, @srcs, @dsts);
  52my ($src, $dst, $base, $dstDir);
  53
  54my $argCount = scalar @ARGV;
  55if (-d $ARGV[$argCount-1]) {
  56        $dstDir = $ARGV[$argCount-1];
  57        @srcArgs = @ARGV[0..$argCount-2];
  58        
  59        foreach $src (@srcArgs) {
  60                $base = $src;
  61                $base =~ s/^.*\///;
  62                $dst = "$dstDir/". $base;
  63                push @dstArgs, $dst;
  64        }
  65}
  66else {
  67    if ($argCount != 2) {
  68        print "Error: moving to directory '"
  69            . $ARGV[$argCount-1]
  70            . "' not possible; not exisiting\n";
  71        usage;
  72    }
  73    @srcArgs = ($ARGV[0]);
  74    @dstArgs = ($ARGV[1]);
  75    $dstDir = "";
  76}
  77
  78my (@allfiles,@srcfiles,@dstfiles);
  79my $safesrc;
  80my %overwritten;
  81
  82$/ = "\0";
  83open(F,"-|","git-ls-files","-z")
  84        or die "Failed to open pipe from git-ls-files: " . $!;
  85
  86@allfiles = map { chomp; $_; } <F>;
  87close(F);
  88
  89
  90my ($i, $bad);
  91while(scalar @srcArgs > 0) {
  92    $src = shift @srcArgs;
  93    $dst = shift @dstArgs;
  94    $bad = "";
  95
  96    if ($opt_v) {
  97        print "Checking rename of '$src' to '$dst'\n";
  98    }
  99
 100    unless (-f $src || -l $src || -d $src) {
 101        $bad = "bad source '$src'";
 102    }
 103
 104    $overwritten{$dst} = 0;
 105    if (($bad eq "") && -e $dst) {
 106        $bad = "destination '$dst' already exists";
 107        if (-f $dst && $opt_f) {
 108            print "Warning: $bad; will overwrite!\n";
 109            $bad = "";
 110            $overwritten{$dst} = 1;
 111        }
 112    }
 113    
 114    if (($bad eq "") && ($src eq $dstDir)) {
 115        $bad = "can not move directory '$src' into itself";
 116    }
 117
 118    if ($bad eq "") {
 119        $safesrc = quotemeta($src);
 120        @srcfiles = grep /^$safesrc(\/|$)/, @allfiles;
 121        if (scalar @srcfiles == 0) {
 122            $bad = "'$src' not under version control";
 123        }
 124    }
 125
 126    if ($bad ne "") {
 127        if ($opt_k) {
 128            print "Warning: $bad; skipping\n";
 129            next;
 130        }
 131        print "Error: $bad\n";
 132        usage();
 133    }
 134    push @srcs, $src;
 135    push @dsts, $dst;
 136}
 137
 138# Final pass: rename/move
 139my (@deletedfiles,@addedfiles,@changedfiles);
 140while(scalar @srcs > 0) {
 141    $src = shift @srcs;
 142    $dst = shift @dsts;
 143
 144    if ($opt_n || $opt_v) { print "Renaming $src to $dst\n"; }
 145    if (!$opt_n) {
 146        rename($src,$dst)
 147            or die "rename failed: $!";
 148    }
 149
 150    $safesrc = quotemeta($src);
 151    @srcfiles = grep /^$safesrc(\/|$)/, @allfiles;
 152    @dstfiles = @srcfiles;
 153    s/^$safesrc(\/|$)/$dst$1/ for @dstfiles;
 154
 155    push @deletedfiles, @srcfiles;
 156    if (scalar @srcfiles == 1) {
 157        if ($overwritten{$dst} ==1) {
 158            push @changedfiles, $dst;
 159        } else {
 160            push @addedfiles, $dst;
 161        }
 162    }
 163    else {
 164        push @addedfiles, @dstfiles;
 165    }
 166}
 167
 168if ($opt_n) {
 169        print "Changed  : ". join(", ", @changedfiles) ."\n";
 170        print "Adding   : ". join(", ", @addedfiles) ."\n";
 171        print "Deleting : ". join(", ", @deletedfiles) ."\n";
 172        exit(1);
 173}
 174        
 175my $rc;
 176if (scalar @changedfiles >0) {
 177        $rc = system("git-update-index","--",@changedfiles);
 178        die "git-update-index failed to update changed files with code $?\n" if $rc;
 179}
 180if (scalar @addedfiles >0) {
 181        $rc = system("git-update-index","--add","--",@addedfiles);
 182        die "git-update-index failed to add new names with code $?\n" if $rc;
 183}
 184$rc = system("git-update-index","--remove","--",@deletedfiles);
 185die "git-update-index failed to remove old names with code $?\n" if $rc;