This is the mail archive of the libc-hacker@sourceware.cygnus.com mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

patch-generating script



This script streamlines the awkward process of generating patches when
you don't have write access to CVS but you need to add or delete
files.  It also handles fixing up the patch headers and ensures that
you don't forget the -u switch.

Docs are at the end, in perl's pod format.  Do 

pod2man mkpatch |nroff -man |more

to get a nice formatted version.

zw

#! /usr/bin/perl -w

# Usage: mkpatch files... >patch
# files can be prepended by + or - to indicate addition or deletion.

die "usage: mkpatch files... >patch\n" if $#ARGV == -1;

@adds = ();
@dels = ();
@chgs = ();

for(@ARGV)
{
    push(@adds, $_), next if s/^\+//;
    push(@dels, $_), next if s/^-//;
    push(@chgs, $_);
}

# Find the base directory.  It is identified by the presence of INSTALL.

$basedir = '';
$upwards = '';
while(! -e "${upwards}INSTALL")
{
    ($ud, $ui) = stat ($upwards || '.');
    $upwards .= '../';
    opendir(D, $upwards);
    @files = map { $upwards . $_ } readdir D;
    closedir D;
    for(@files)
    {
	next if /\/\.\.?$/;
	($d, $i) = stat $_;
	if($i == $ui && $d == $ud)
	{
	    $_ =~ s/(\.\.\/)+//;
	    $basedir = $_ . '/' . $basedir;
	    last;
	}
    }
}

$eqs = '=' x 60 . "\n";

# First the files that have changed.
if($#chgs != -1)
{
    open(CVS, "cvs diff -u" . join(' ', @chgs) . ' |');
    while(<CVS>)
    {
	s=^Index:\s+((?:[^/]+/)*([^/]+))$=Index: $basedir$1= && do
	{
	    $full = $basedir . $1;
	    $small = $2;
	    chomp $full; chomp $small;
	    print $eqs;
	    next;
	};
	
	(/^[=Rrd]/) && do { $_ = <CVS>; redo; };
	
	/^(?:---|\+\+\+) / && do
	{
	    s/\.orig//;
	    s/\([a-zA-Z0-9\/._\s-]+\)//;
	    s/\s\Q$small\E/ $full/ if $small;
	    s/\Q$full\E\s+/$full\t/;
	};
    }
    continue { print; }
    close CVS;
}

# Now the files added.
for $f (@adds)
{
    print $eqs;
    print "Index: $basedir$f\n";
    open(DIFF, "diff -u /dev/null $f |");

    $_ = <DIFF>;
    print "--- $basedir$f\tcreated\n";

    $_ = <DIFF>;
    s/^\Q+++ /+++ $basedir/;
    s!$!\t1.1!;
    print;

    while(<DIFF>) { print; }
    close DIFF;
}

# Finally those deleted.

if($#dels != -1)
{
    open(CVS, 'cvs status ' . join(' ', @dels) . ' |');
    while(<CVS>)
    {
	/^File:\s(\S+)/ and $file = $1;
	/Working revision:\s+([0-9.]+)/ and $rev{$file} = $1;
    }
    close CVS;
    
    for $f (@dels)
    {
	($file) = $f =~ m!([^/]+)$!;
	
	open(DIFF, "diff -u $f /dev/null |");

	print $eqs;
	print "Index: $basedir$f\n";
	$_ = <DIFF>;
	s/ \Q$f\E\s+/ $basedir$f\t/;
	s/$/\t$rev{$file}/;
	print;
	$_ = <DIFF>;
	print "+++ $basedir$f\tremoved\n";
	
	while(<DIFF>) { print; }
	close DIFF;
    }
}

__END__

=head1 NAME

mkpatch - Make patches against a CVS tree.

=head1 SYNOPSIS

B<mkpatch files...>

=head1 DESCRIPTION

I<mkpatch> takes a list of files on the command line and generates a
canonical-format diff of the files against the CVS repository they
came from.  The diff is sent to stdout.

Files whose names have C<+> prepended are treated as just-created
files not yet in CVS.  The diff will include the output of C<diff>
C<-u> C</dev/null> I<file> for those files.  Files whose names have C<->
prepended are treated as files to be deleted from the source tree.
Those files have the output of C<diff> C<-u> I<file> C</dev/null>
included in the output.

Canonical diff format is unified diff format with the headers munged
so that I<patch> will not get confused applying the diff.  (I<cvs>
drops the directory path from the C<---> and C<+++> header lines;
older versions of I<patch> can't handle this.)A sample header is as
follows:

    ============================================================
    Index: posix/sys/types.h
    --- posix/sys/types.h	1998/06/12 14:32:19	1.219
    +++ posix/sys/types.h	1998/07/01 02:10:42

The row of equal signs is a visual separator to make reading the diff
easier. 

If I<mkpatch> is invoked in a subdirectory of the source tree, it will
still make the diff relative to the root.  It does this by finding the
path to the current directory from the root, and prepending that path
to all files in the diff.  This is a convenience for when you have
changed many files in a deep subdirectory and don't want to type the
path from the root over and over again.  The root is defined as the
nearest enclosing directory that contains a file named C<INSTALL>,
which is correct for most GNUish source trees.

=head1 BUGS

I<mkpatch> parses the output of I<cvs> and I<diff>.  If that output
changes it is likely to break.

The datestamp on changed files is formatted differently from that on
added or removed files.

There is no way to include a file whose name begins with C<+> or C<->
in the diff.

=head1 SEE ALSO

cvs(1), diff(1), patch(1)

=cut



Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]