rewritegitmoves 3.17 KB
Newer Older
1 2 3
#!/usr/bin/perl -w
#
# Copyright (c) 2011 University of Utah and the Flux Group.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# 
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
#

#
# Generate a script that moves all files in a given directory from any old
# locations they may have been in into their current locations. For use with
# git filter-branch --tree-filter - the idea is that you use this script on
# a directrory that has had files moved into it, and it generates a script for
# you that re-does the moves. You then pass the generated script to
# --tree-filter, which will make the move happen in all of history, so that you
# can pretend the files were always in their current locations.
#
# eg.
# git-rewritemoves clientside > ~/mvscript
# git filter-branch --tree-filter 'perl ~/mvscript' HEAD
#

use strict;
sub get_filemoves($);

if (@ARGV != 1) { 
    die "Usage: $0 <dirname>\n";
}
my ($dirname) = @ARGV;

my @moves;

my @files = `find $dirname -type f`;
my $count = 0;
foreach my $file (@files) {
    chomp $file;

    $count++;
    print STDERR "Checking file $file ($count / " . scalar(@files) . ")\n";

    my @filemoves = get_filemoves($file);
    push @moves, @filemoves;
}

#
# Now, output a perl script that executes the moves
#
print << 'HEADER';
#!/usr/bin/perl -w
my $errors = 0;
sub move($$) {
    my ($src,$dst) = @_;
    my $destdir = `dirname $dst`;
    chomp $destdir;
    if (!-d $destdir) {
        system("mkdir -p $destdir") && $errors++;
    }
    system("mv $src $dst") && $errors++;
}

HEADER

foreach my $move (@moves) {
    my ($src, $dst) = @$move;
    print "if (-e \"$src\") { move(\"$src\",\"$dst\"); }\n";
}

print << 'FOOTER';
exit ($errors != 0);
FOOTER

#
# Get a list of moves from old locations for the given file to
# the current location. Return this as a list of array refs, each
# of which is pair of filenames
#
sub get_filemoves($) {
    my ($file) = @_;

    # Use git log to find all filenames the file has had in the past
    my @log_output = `git log --name-only --pretty="format:" --follow $file`;
    chomp(@log_output);

    # Remove blank lines
    @log_output = grep {$_} @log_output;

    my @moves;

    # We always move files to their current location, not intermediate ones
    my $current_loc = shift @log_output;
    my $next_loc = $current_loc;
    foreach my $loc (@log_output) {
        if ($loc ne $next_loc) {
            # A move happened
            push @moves, [$loc,$current_loc];
            print STDERR "   Move: $loc -> $current_loc\n";
            $next_loc = $loc;
        }
    }

    return @moves;
}