1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
|
#!/usr/bin/perl -w
# p4genpatch - Generate a perl patch from the repository
# Usage: $0 -h
# andreas.koenig@anima.de
use strict;
use File::Temp qw(tempdir);
use File::Compare;
use File::Spec;
use File::Spec::Unix;
use Time::Local;
use Getopt::Long;
use Cwd qw(cwd);
sub correctmtime ($$$);
sub Usage ();
$0 =~ s|^.*[\\/]||;
my $VERSION = '0.05';
my $TOPDIR = cwd();
my @P4opt;
our %OPT = ( "d" => "u", b => "//depot/perl/", "D" => "diff" );
Getopt::Long::Configure("no_ignore_case");
GetOptions(\%OPT, "b=s", "p=s", "d=s", "D=s", "h", "v", "V") or die Usage;
print Usage and exit if $OPT{h};
print "$VERSION\n" and exit if $OPT{V};
die Usage unless @ARGV == 1 && $ARGV[0] =~ /^\d+$/;
my $CHANGE = shift;
for my $p4opt (qw(p)) {
push @P4opt, "-$p4opt $OPT{$p4opt}" if $OPT{$p4opt};
}
my $system = "p4 @P4opt describe -s $CHANGE |";
open my $p4, $system or die "Could not run $system";
my @action;
while (<$p4>) {
print;
next unless m|($OPT{b})|;
my($prefix) = $1;
$prefix =~ s|/$||;
$prefix =~ s|/[^/]+$||; # up to the last "/" in the match is to be stripped
if (my($file,$action) = m|^\.\.\. (//depot.*)\s(\w+)$|) {
next if $action eq "delete";
push @action, [$action, $file, $prefix];
}
}
close $p4;
my $tempdir;
my @unlink;
print "Differences ...\n";
for my $a (@action) {
$tempdir ||= tempdir( "tmp-XXXX", CLEANUP => 1, TMPDIR => 1 );
@unlink = ();
my($action,$file,$prefix) = @$a;
my($path,$basename,$number) = $file =~ m|\Q$prefix\E/(.+/)?([^/]+)#(\d+)|;
my @splitdir = File::Spec::Unix->splitdir($path);
$path = File::Spec->catdir(@splitdir);
my($depotfile) = $file =~ m|^(.+)#\d+\z|;
die "Panic: Could not parse file[$file]" unless $number;
$path = "" unless defined $path;
my($d1,$d2,$prev,$prevchange,$prevfile,$doadd,$t1,$t2);
$prev = $number-1;
$prevchange = $CHANGE-1;
# can't assume previous rev == $number-1 due to obliterated revisions
$prevfile = "$depotfile\@$prevchange";
if ($number == 1 or $action =~ /^(add|branch)$/) {
$d1 = $^O eq 'MacOS' ? File::Spec->devnull : "/dev/null";
$t1 = $d1;
++$doadd;
} elsif ($action =~ /^(edit|integrate)$/) {
$d1 = File::Spec->catfile($path, "$basename-$prevchange");
$t1 = File::Spec->catfile($tempdir, $d1);
warn "==> $d1 <==\n" if $OPT{v};
my $system = qq[p4 @P4opt print -o "$t1" "$prevfile"];
my $status = `$system`;
if ($?) {
warn "$0: system[$system] failed, status[$?]\n";
next;
}
chmod 0644, $t1;
if ($status =~ /\#(\d+) \s - \s \w+ \s change \s (\d+) \s /x) {
($prev,$prevchange) = ($1,$2);
$prevfile = "$depotfile#$prev";
my $oldd1 = $d1;
$d1 =~ s/-\d+$/#$prev~$prevchange~/;
my $oldt1 = $t1;
$t1 = File::Spec->catfile($tempdir, $d1);
rename $oldt1, $t1;
}
push @unlink, $t1;
} else {
die "Unknown action[$action]";
}
$d2 = File::Spec->catfile($path, $basename);
$t2 = File::Spec->catfile($tempdir, $d2);
push @unlink, $t2;
warn "==> $d2#$number <==\n" if $OPT{v};
my $system = qq[p4 @P4opt print -o "$t2" "$file"];
# warn "system[$system]";
my $type = `$system`;
if ($?) {
warn "$0: `$system` failed, status[$?]\n";
next;
}
chmod 0644, $t2;
$type =~ m|^//.*\((.+)\)$| or next;
$type = $1;
if ($doadd or File::Compare::compare($t1, $t2)) {
print "\n==== $file ($type) ====\n";
unless ($type =~ /text/) {
next;
}
unless ($^O eq 'MacOS') {
$d1 =~ s,\\,/,g;
$d2 =~ s,\\,/,g;
}
print "Index: $d2\n";
correctmtime($prevfile,$prev,$t1) unless $doadd;
correctmtime($file,$number,$t2);
chdir $tempdir or warn "Could not chdir '$tempdir': $!";
$system = qq[$OPT{D} -$OPT{d} "$d1" "$d2"];
system($system); # no return check because diff doesn't always return 0
chdir $TOPDIR or warn "Could not chdir '$TOPDIR': $!";
}
}
continue {
for (@unlink) {
unlink or warn "Could not unlink $_: $!" if -f;
}
}
print "End of Patch.\n";
my($tz_offset);
sub correctmtime ($$$) {
my($depotfile,$nr,$localfile) = @_;
my %fstat = map { /^\.\.\. (\w+) (.*)$/ } `p4 @P4opt fstat -s "$depotfile"`;
return unless exists($fstat{headRev}) and $fstat{headRev} == $nr;
if ($^O eq 'MacOS') { # fix epoch ... still off by three hours (EDT->PDT)
require Time::Local;
$tz_offset ||= sprintf "%+0.4d\n", (
Time::Local::timelocal(localtime) - Time::Local::timelocal(gmtime)
);
$fstat{headTime} += 2082844801 + $tz_offset;
}
utime $fstat{headTime}, $fstat{headTime}, $localfile;
}
sub Usage () {
qq{Usage: $0 [OPTIONS] patchnumber
-p host:port p4 port (e.g. myhost:1666)
-d diffopt option to pass to diff(1)
-D diff diff(1) to use
-b branch(es) which branches to include (regex); the last
directory within the matched part will be
preserved on the local copy, so that patch -p1
will work (default: "//depot/perl/")
-v verbose
-h print this help and exit
-V print version number and exit
Fetches all required files from the repository, puts them into a
temporary directory with sensible names and sensible modification
times and composes a patch to STDOUT using external diff command.
Requires repository access.
Examples:
perl $0 12345 | gzip -c > 12345.gz
perl $0 -dc 12345 > change-12345.patch
perl $0 -b //depot/maint-5.6/perl -v 8571 > 8571
};
}
|