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;