diff options
author | Josef Weidendorfer <Josef.Weidendorfer@gmx.de> | 2005-10-23 18:15:34 +0200 |
---|---|---|
committer | Junio C Hamano <junkio@cox.net> | 2005-10-23 17:25:08 -0700 |
commit | 1114b26e8f2d06912d855c631e51a4ee8a06c4e2 (patch) | |
tree | c043f0993afe5c057409ef748ce313c89c20c7dc /git-mv.perl | |
parent | Silence confusing and false-positive curl error message (diff) | |
download | tgif-1114b26e8f2d06912d855c631e51a4ee8a06c4e2.tar.xz |
Add git-mv
It supersedes git-rename by adding functionality to move multiple
files, directories or symlinks into another directory. It also
provides according documentation.
The implementation renames multiple files, using the arguments from
the command line to produce an array of sources and destinations. In
a first pass, all requested renames are checked for errors, and
overwriting of existing files is only allowed with '-f'. The actual
renaming is done in a second pass. This ensures that any error
condition is checked before anything is changed.
Signed-off-by: Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
Diffstat (limited to 'git-mv.perl')
-rwxr-xr-x | git-mv.perl | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/git-mv.perl b/git-mv.perl new file mode 100755 index 0000000000..28bced9595 --- /dev/null +++ b/git-mv.perl @@ -0,0 +1,185 @@ +#!/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> <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 +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 (@srcArgs, @dstArgs, @srcs, @dsts); +my ($src, $dst, $base, $dstDir); + +my $argCount = scalar @ARGV; +if (-d $ARGV[$argCount-1]) { + $dstDir = $ARGV[$argCount-1]; + @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"; + usage; + } + @srcArgs = ($ARGV[0]); + @dstArgs = ($ARGV[1]); + $dstDir = ""; +} + +my (@allfiles,@srcfiles,@dstfiles); +my $safesrc; +my %overwritten; + +$/ = "\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'"; + } + + $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 (($bad eq "") && ($src eq $dstDir)) { + $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"; + } + } + + if ($bad ne "") { + if ($opt_k) { + print "Warning: $bad; skipping\n"; + next; + } + print "Error: $bad\n"; + usage(); + } + push @srcs, $src; + push @dsts, $dst; +} + +# Final pass: rename/move +my (@deletedfiles,@addedfiles,@changedfiles); +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: $!"; + } + + $safesrc = quotemeta($src); + @srcfiles = grep /^$safesrc(\/|$)/, @allfiles; + @dstfiles = @srcfiles; + s/^$safesrc(\/|$)/$dst$1/ for @dstfiles; + + push @deletedfiles, @srcfiles; + if (scalar @srcfiles == 1) { + if ($overwritten{$dst} ==1) { + push @changedfiles, $dst; + } else { + push @addedfiles, $dst; + } + } + else { + push @addedfiles, @dstfiles; + } +} + +if ($opt_n) { + print "Changed : ". join(", ", @changedfiles) ."\n"; + print "Adding : ". join(", ", @addedfiles) ."\n"; + 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; +} +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; +} +$rc = system("git-update-index","--remove","--",@deletedfiles); +die "git-update-index failed to remove old names with code $?\n" if $rc; |