- #!/usr/bin/perl -w
- =head1 NAME
- p4d2p - convert diffs from Perforce to patch format
- =head1 SYNOPSIS
- p4d2p [-hiv] [-o comment] [-p command] [files ...]
- =head2 Options:
- -h Display this help message
- -i[suffix] Edit files in place; append suffix to original names
- -o comment Output the comment instead of the old file version
- -p command Use the command instead of `p4 print -q` to get files
- -v Output progress messages
- =head1 DESCRIPTION
- Change diffs in Perforce format (from `p4 diff`, `p4 diff2` or `p4 describe`)
- to a form suitable for input to the GNU `patch` program.
- Insert headers and diffs, but don't remove anything.
- Edit each file in place if file names are given;
- otherwise filter from input to output.
- =head1 EXAMPLES
- p4 diff -du ...@ver8_0 | p4d2p -o "-r v80" | patch -p3
- p4 diff2 -dc ...@Beta1 ...@Beta2 | p4d2p -o "-r Beta1" | patch -p5
- p4 describe -du 123 | p4d2p -o "@122" | patch -p2
- =head1 KNOWN BUGS
- RCS format diffs (from `p4 diff -dn`) are not converted.
- =head1 SEE ALSO
- To generate a patch from a change number:
- L<http://public.activestate.com/gsar/APC/perl-current/Porting/p4genpatch>
- To generate a patch for added files:
- L<http://public.activestate.com/gsar/APC/perl-5.6.x/Porting/p4desc>
- Perforce L<http://www.perforce.com/perforce/technical.html>
- GNU `patch` L<http://www.fsf.org/manual/diffutils-2.8.1/html_node/Multiple-Patches.html>
- =head1 AUTHOR
- John Kristian <jkristian@docent.com>.
- Thanks to Gurusamy Sarathy for inspiration; but I accept all blame.
- =cut
- use Getopt::Std;
- use POSIX qw(strftime);
- use Time::Local qw(timegm);
- use vars qw($opt_h $opt_i $opt_o $opt_p $opt_v);
- if (!getopts('hi::o:p:v') || $opt_h) {
- open(STDOUT, '>&STDERR');
- exec('perldoc', $0);
- exit(1); # exec failed
- }
- if (not @ARGV) {@ARGV = '-'; undef $^I;}
- elsif (defined($opt_i)) {$^I = $opt_i; warn "-i$opt_i\n" if $opt_v;}
- # else $^I comes from Perl's -i option; e.g. perl -i.bak p4d2p ...
- my $Z = timegm(localtime(0)) / 60;
- $Z = ($Z >= 0) ? sprintf('+%02d%02d', $Z/60, $Z%60)
- : sprintf('-%02d%02d',-$Z/60,-$Z%60);
- my $timeFormat = "%Y-%m-%d %H:%M:%S $Z"; # ISO 8601
- my $now = strftime($timeFormat, localtime);
- my $epoch = "1970-01-01 00:00:00Z";
- my ($oldFile, $oldNote, $newFile, $newNote, @movedFiles);
- while (<>) {
- # `p4 diff` header
- if (m<^==== //(.+?)(\#\d+) +- +(.+?) +====( \(.+\))?$>) {
- ($oldFile, $oldNote, $newFile) = ($1, $2, $3);
- $oldNote = " " . ($opt_o || $oldNote);
- my (@stat) = stat($newFile);
- $newNote = ((@stat && defined($stat[9]))
- ? strftime($timeFormat, localtime($stat[9])) # time last modified
- : $now);
- $newNote = " $newNote";
- }
- # `p4 diff2` header
- elsif (m<^==== //(.+?)(\#\d+) +\(.+?\) +- +(.+?)(\#\d+) +\(.+?\) +==== +\w+$>) {
- ($oldFile, $oldNote, $newFile, $newNote) = ($1, $2, $3, $4);
- $oldNote = " " . ($opt_o || $oldNote);
- $newNote = " $newNote";
- }
- # `p4 describe` header
- elsif (m<^==== //(.+?)(\#\d+) .*?====( \(.+\))?$>) {
- ($newFile, $newNote) = ($1, $2);
- $oldFile = $newFile;
- $oldNote = " " . ($opt_o ||
- (($newNote =~ m<\#(\d+)>)
- && "#" . ($1-1))); # the previous version
- $newNote = " $newNote";
- }
- # unified diff (the preferred format for `patch`)
- elsif (defined($oldFile) && m<^\@\@\s.*\s\@\@$>) {
- warn "emitting diff -u header\n" if $opt_v;
- print("Index: //$oldFile\n", "--- $oldFile$oldNote\n", "+++ $oldFile$newNote\n");
- undef $oldFile;
- }
- # context diff
- elsif (defined($oldFile) && m<^\*+$>) {
- warn "emitting diff -c header\n" if $opt_v;
- print("Index: //$oldFile\n", "*** $oldFile$oldNote\n", "--- $oldFile$newNote\n");
- undef $oldFile;
- }
- # default diff (not recommended for `patch`)
- elsif (defined($oldFile) && m<^\d+(,\d+)?[acd]\d+>) {
- warn "emitting diff header (not recommended for `patch`)\n" if $opt_v;
- print("Index: $oldFile\n", "diff -r //$oldFile $newFile\n");
- undef $oldFile;
- }
- # `p4 describe` add, branch or delete
- elsif (m<^\.\.\. (//.+?\#\d+ (add|branch|delete))$>) {
- push @movedFiles, $1;
- }
- } continue {
- print; # echo all input
- if (eof) {
- if (@movedFiles) {
- my ($verb, @file);
- for (@movedFiles) {
- ($newFile, $newNote, $verb) = m<//(.+?)(\#\d+) (\w*)$>;
- warn $verb if $opt_v;
- $oldFile = $newFile;
- if ($verb eq 'delete') {
- $oldNote = "#" . (($newNote =~ m<\#(\d+)>) && ($1-1)); # the previous version
- $newNote = $epoch;
- @file = p4print("//$oldFile$oldNote");
- } else { # add or branch
- $oldNote = $epoch;
- @file = p4print("//$newFile$newNote");
- }
- if (!$? && @file) {
- $oldNote = " " . ($opt_o || $oldNote);
- $newNote = " $newNote";
- my $lines = (@file <= 1) ? "" : ("," . @file);
- print("\nIndex: //$newFile\n", "--- $oldFile$oldNote\n", "+++ $newFile$newNote\n");
- if ($verb eq 'delete') {
- print("\@\@ -1$lines +0,0 \@\@\n");
- print(join("-", "", @file));
- } else { # add or branch
- print("\@\@ -0,0 +1$lines \@\@\n");
- print(join("+", "", @file));
- }
- print("\n\\ No newline at end of file\n") if ($file[$#file] !~ m<\n$>);
- }
- }
- @movedFiles = ();
- }
- undef $oldFile;
- }
- }
- sub p4print {
- my ($name) = @_;
- my $cmd = ($opt_p || "p4 print -q")." \"$name\"";
- # Sadly, executing `p4 print` will consume some input.
- # Which is one reason not to emit files immediately
- # upon reading their names from the input stream.
- my @file = `$cmd`;
- warn "status $? from $cmd" if ($?);
- return @file;
- }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#9 | 3909 | John Kristian |
Added option -i (similar to perl -i). Use the local time zone for output times. |
21 years ago | |
#8 | 3887 | John Kristian | Made $timeFormat more human-readable (and more like GNU `diff`). Output '1970-01-01 00:00...:00Z' as the $oldNote of an added file or $newNote of a deleted file. « |
21 years ago | |
#7 | 3886 | John Kristian | renamed variables | 21 years ago | |
#6 | 3885 | John Kristian | improved POD | 21 years ago | |
#5 | 3884 | John Kristian | output a patch for deleted files, too | 21 years ago | |
#4 | 3883 | John Kristian | output a patch for added files, too | 21 years ago | |
#3 | 3882 | John Kristian | improved POD | 21 years ago | |
#2 | 3881 | John Kristian | Combined the interface (command line syntax) of p4d2p with the implementation of p4diff2pa...tch. « | 21 years ago | |
#1 | 3879 | John Kristian | http://search.cpan.org/src/JHI/perl-5.8.1-RC4/Porting/p4d2p | 21 years ago |