: #-*- Perl -*- ### cvs-diff --- front end to `cvs diff' ## Copyright (C) 2001 Ben Wing. ## Author: Ben Wing ## Based on: Earlier version by Martin Buchholz ## Maintainer: Ben Wing ## Current Version: 1.1, April 6, 2001 ## This file is part of XEmacs. ## XEmacs is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2, or (at your option) ## any later version. ## XEmacs 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 ## General Public License for more details. ## You should have received a copy of the GNU General Public License ## along with XEmacs; see the file COPYING. If not, write to the Free ## Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ## 02111-1307, USA. eval 'exec perl -w -S $0 ${1+"$@"}' # Portability kludge if 0; use strict; use Cwd; use Getopt::Long; (my $myName = $0) =~ s@.*/@@; my $usage=" Usage: $myName [-no-changelog] [diff-arg ...] [files ...] Front end to \`cvs diff'. Does the following in addition to a simple \`cvs diff': -- removes ChangeLog diffs from the diff output, and (unless -no-changelog was specified) prepends the entries to the beginning of the diff. This is useful for submitting a diff to the maintainers. -- removes the generated files auto-autoloads.el, custom-load.el, depend, and configure from the output. -- fixes CVS bug when specifying files to diff in multiple directories. -- hacks the line used by `patch' to contain the directory relative to the workspace root, so you can easily apply this patch to another workspace. "; my $debug = defined $ENV{VERBOSE} || defined $ENV{DEBUG}; sub ParsePath { my $pos = rindex ($_[0], "/"); return ($pos > 0 ? (substr ($_[0], 0, $pos), substr ($_[0], $pos+1)) : $pos == -1 ? ('.', $_[0]) : ("/", substr ($_[0], 1))); } # Find the current directory relative to the root of the CVS workspace. die "Not a CVS directory tree.\n" unless -d "CVS"; my $root = cwd (); my $rootrel = ""; while (-d "$root/../CVS") { ($root, my $root_component) = ParsePath ($root); $rootrel = "$root_component/$rootrel"; } my @SAVE_ARGV = @ARGV; # Extract out our own and all diff options so that we can reliably get # the actual files to `cvs diff'. my %options = (); $Getopt::Long::ignorecase = 0; &GetOptions ( \%options, # our own options 'no-changelog', # diff options 'l', 'R', 'D=s', 'N', 'r=s', 'ifdef=s', 'i', 'ignore-case', 'w', 'ignore-all-space', 'b', 'ignore-space-change', 'B', 'ignore-blank-lines', 'I=s', 'ignore-matching-lines=s', 'binary', 'a', 'text', 'c', 'C=i', 'context:i', 'u', 'U=i', 'unified:i', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'L=s', 'label=s', 'p', 'show-c-function', 'F=s', 'show-function-line=s', 'q', 'brief', 'e', 'ed', 'n', 'rcs', 'y', 'side-by-side', 'w:i', 'width=i', 'left-column', 'suppress-common-lines', # 'DNAME', 'ifdef=s', overridden by CVS 'old-group-format=s', 'new-group-format=s', 'unchanged-group-format=s', 'changed-group-format=s', 'line-format=s', 'old-line-format=s', 'new-line-format=s', 'unchanged-line-format=s', # 'l', overridden by CVS 'paginate', 't', 'expand-tabs', 'T', 'initial-tab', # 'r', overridden by CVS 'recursive', # 'N', overridden by CVS 'new-file', 'P', 'unidirectional-new-file', 's', 'report-identical-files', 'x=s', 'exclude=s', 'X=s', 'exclude-from=s', 'S=s', 'starting-file=s', 'horizon-lines=i', 'd', 'minimal', 'H', 'speed-large-files', 'v', 'version', 'help', ); die $usage if $options{"help"}; # extract options, but eliminate --no-changelog my @OPTIONS = grep (!/(-|--|\+)no-changelog/, @SAVE_ARGV[0 .. (@SAVE_ARGV - @ARGV - 1)]); # print "ARGV: @ARGV\n"; # print "SAVE_ARGV: @SAVE_ARGV\n"; # print "OPTIONS: @OPTIONS\n"; select(STDOUT); $| = 1; # now output the ChangeLogs. @ARGV = ('.') if (! @ARGV); if (!$options{"no-changelog"}) { foreach my $file (@ARGV) { if (-d $file) { chomp (my @changelogs = `find $file -name ChangeLog -print`); foreach my $changelog (@changelogs) { # use cvs-mods to filter out non-modified ChangeLogs; this greatly # speeds things up if (`cvs-mods $changelog`) { # print "Checking $changelog\n"; open (CHANGELOG, "cvs diff -w @OPTIONS $changelog | sed -n 's/^\+//p' | grep -v '^\\+\\+\\+' |"); if (my @slurp = ) { print "\n$changelog:\n\n"; print @slurp; } } close (CHANGELOG); } } } } print "\n"; # this construct forks a subprocess, with the parent reading the # child's output and the child executing the code within braces. if (!open (CVS, "-|")) { open STDERR, ">&STDOUT"; # cvs has an evil bug that if I give a list of files containing # both files in the current directory and in subdirectories, cvs # ignores the files in the current directory. simplest just to # call `cvs diff' once per file. foreach my $file (@ARGV) { # do not use -w option here by default -- explicitly specify it if # you want it. Otherwise, you end up with output that looks right # but will fail in all sorts of random ways if you try to use # `patch' with it. Furthermore, it doesn't actually make the output # much (if any) shorter in most cases. die "Can't exec @_: $!\n" if system ('cvs', 'diff', @OPTIONS, $file) < 0; } exit 0; } my $file; my $print = 1; while () { # track files as they go by. if (/^Index: (.*)/) { $file = $1; # look for files to remove. $print = !($file =~ /(ChangeLog|auto-autoloads.el|custom-load.el|depend|configure)$/); } # hack patch file lines to include the directory. s!^(---|\+\+\+) (\S+)!$1 $rootrel$file!; print if $print; }