summaryrefslogtreecommitdiff
path: root/Porting/p4genpatch
blob: 543baa9815107eda878f08e669fb32cab077e2d9 (plain)
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
};
}