diff options
Diffstat (limited to 'git-mv.perl')
-rwxr-xr-x | git-mv.perl | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/git-mv.perl b/git-mv.perl new file mode 100755 index 0000000000..65b1dcfdf2 --- /dev/null +++ b/git-mv.perl @@ -0,0 +1,224 @@ +#!/usr/bin/perl +# +# Copyright 2005, Ryan Anderson <ryan@michonline.com> +# Josef Weidendorfer <Josef.Weidendorfer@gmx.de> +# +# This file is licensed under the GPL v2, or a later version +# at the discretion of Linus Torvalds. + + +use warnings; +use strict; +use Getopt::Std; + +sub usage() { + print <<EOT; +$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 "Error: git repository not found."; + exit(1); +} + + +our ($opt_n, $opt_f, $opt_h, $opt_k, $opt_v); +getopts("hnfkv") || usage; +usage() if $opt_h; +@ARGV >= 1 or usage; + +my (@srcArgs, @dstArgs, @srcs, @dsts); +my ($src, $dst, $base, $dstDir); + +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/^.*\///; + $dst = "$dstDir/". $base; + push @dstArgs, $dst; + } +} +else { + if ($argCount != 2) { + print "Error: moving to directory '" + . $ARGV[$argCount-1] + . "' not possible; not exisiting\n"; + exit(1); + } + @srcArgs = ($ARGV[0]); + @dstArgs = ($ARGV[1]); + $dstDir = ""; +} + +my (@allfiles,@srcfiles,@dstfiles); +my $safesrc; +my (%overwritten, %srcForDst); + +$/ = "\0"; +open(F,"-|","git-ls-files","-z") + or die "Failed to open pipe from git-ls-files: " . $!; + +@allfiles = map { chomp; $_; } <F>; +close(F); + + +my ($i, $bad); +while(scalar @srcArgs > 0) { + $src = shift @srcArgs; + $dst = shift @dstArgs; + $bad = ""; + + if ($opt_v) { + print "Checking rename of '$src' to '$dst'\n"; + } + + unless (-f $src || -l $src || -d $src) { + $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 ($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 "") && ($dst =~ /^$src\//)) { + $bad = "can not move directory '$src' into itself"; + } + + if ($bad eq "") { + if (scalar @srcfiles == 0) { + $bad = "'$src' not under version control"; + } + } + + if ($bad eq "") { + if (defined $srcForDst{$dst}) { + $bad = "can not move '$src' to '$dst'; already target of "; + $bad .= "'".$srcForDst{$dst}."'"; + } + else { + $srcForDst{$dst} = $src; + } + } + + if ($bad ne "") { + if ($opt_k) { + print "Warning: $bad; skipping\n"; + next; + } + print "Error: $bad\n"; + exit(1); + } + push @srcs, $src; + push @dsts, $dst; +} + +# 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) { + if (!rename($src,$dst)) { + $bad = "renaming '$src' failed: $!"; + if ($opt_k) { + print "Warning: skipped: $bad\n"; + $bad = ""; + next; + } + last; + } + } + + $safesrc = quotemeta($src); + @srcfiles = grep /^$safesrc(\/|$)/, @allfiles; + @dstfiles = @srcfiles; + s/^$safesrc(\/|$)/$dst$1/ for @dstfiles; + + push @deletedfiles, @srcfiles; + if (scalar @srcfiles == 1) { + # $dst can be a directory with 1 file inside + if ($overwritten{$dst} ==1) { + push @changedfiles, $dstfiles[0]; + + } else { + push @addedfiles, $dstfiles[0]; + } + } + else { + push @addedfiles, @dstfiles; + } +} + +if ($opt_n) { + if (@changedfiles) { + print "Changed : ". join(", ", @changedfiles) ."\n"; + } + if (@addedfiles) { + print "Adding : ". join(", ", @addedfiles) ."\n"; + } + if (@deletedfiles) { + print "Deleting : ". join(", ", @deletedfiles) ."\n"; + } +} +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 ($bad ne "") { + print "Error: $bad\n"; + exit(1); +} |