From 56263711969a4fb46eb65b91b316d1be96bb5166 Mon Sep 17 00:00:00 2001 From: Dave Goodell Date: Tue, 9 Jul 2013 14:39:41 +0000 Subject: [PATCH] update-my-copyright.pl now works with Git This script now takes command line options: ``` ./update-my-copyright.pl [options] --help | -h This help message --quiet | -q Only output critical messages to stdout --check-only exit(111) if there are files with copyrights to edit --search-name=NAME Set search name to NAME --formal-same=NAME Set formal name to NAME ``` The `--check-only` and `--quiet` options are suitable for use in a git pre-commit script to check for out of date copyright headers. Reviewed by jsquyres This commit was SVN r28742. --- contrib/update-my-copyright.pl | 245 +++++++++++++++++++++++++-------- 1 file changed, 184 insertions(+), 61 deletions(-) diff --git a/contrib/update-my-copyright.pl b/contrib/update-my-copyright.pl index 59a84e65a2..bbb5a45500 100755 --- a/contrib/update-my-copyright.pl +++ b/contrib/update-my-copyright.pl @@ -46,6 +46,21 @@ use strict; use Cwd; +use Getopt::Long; + +# Set to true if the script should merely check for up-to-date copyrights. +# Will exit with status 111 if there are out of date copyrights which this +# script can correct. +my $CHECK_ONLY = 0; +# used by $CHECK_ONLY logic for bookeeping +my $would_replace = 0; + +# Set to true to suppress most informational messages. Only out of date files +# will be printed. +my $QUIET = 0; + +# Set to true if we just want to see the help message +my $HELP = 0; # Defaults my $my_search_name = "Cisco"; @@ -56,13 +71,45 @@ $my_search_name = $ENV{OMPI_COPYRIGHT_SEARCH_NAME} if (defined($ENV{OMPI_COPYRIGHT_SEARCH_NAME})); $my_formal_name = $ENV{OMPI_COPYRIGHT_FORMAL_NAME} if (defined($ENV{OMPI_COPYRIGHT_FORMAL_NAME})); -print "==> Copyright search name: $my_search_name\n"; -print "==> Copyright formal name: $my_formal_name\n"; + +GetOptions( + "help" => \$HELP, + "quiet" => \$QUIET, + "check-only" => \$CHECK_ONLY, + "search-name=s" => \$my_search_name, + "formal-name=s" => \$my_formal_name, +) or die "unable to parse options, stopped"; + +if ($HELP) { + print < Copyright search name: $my_search_name\n"; +quiet_print "==> Copyright formal name: $my_formal_name\n"; # Get the year my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime; $year += 1900; -print "==> This year: $year\n"; +quiet_print "==> This year: $year\n"; # Find the top-level OMPI source tree dir my $start = cwd(); @@ -75,61 +122,29 @@ while (! -f "$top/Makefile.man-page-rules") { } chdir($start); -print "==> Top-level Open MPI dir: $top\n"; -print "==> Current directory: $start\n"; +quiet_print "==> Top-level Open MPI dir: $top\n"; +quiet_print "==> Current directory: $start\n"; -# Are we hg or svn? If we're both hg and svn, assume svn. -my $cmd; -if (-d "$top/.svn") { - $cmd = "svn st ."; -} elsif (-d "$top/.hg") { - $cmd = "hg st ."; -} elsif (-d "$top/.git") { - $cmd = "git status ."; -} else { - die "Can't find SVN or HG meta dirs"; -} +# Select VCS used to obtain modification info. Choose in increasing priority +# order (last hit wins). +my $vcs; +$vcs = "git" + if (-d "$top/.git"); +$vcs = "hg" + if (-d "$top/.hg"); +$vcs = "svn" + if (-d "$top/.svn"); -# Run the command, parsing the output. Make a list of files that are -# added or modified. -print "==> Running: \"$cmd\"\n"; -open(CMD, "$cmd|") || die "Can't run command"; -my @files; -while () { - chomp; - # SVN and HG use this form - if ($_ =~ /^M/ || $_ =~ /^A/) { - my @tokens = split(/\s+/, $_); - # Handle output of both forms: - # M filenameA - # A + filenameB - my $filename = $tokens[1]; - $filename = $tokens[2] - if ($tokens[1] =~ /\+/); - # Don't bother saving directory names - push(@files, $filename) - if (-f $filename); - } - - # Git uses these forms - elsif ($_ =~ m/^#\s+modified:\s+(\S+)$/) { - push(@files, $1) - if (-f $1); - } elsif ($_ =~ m/^#\s+new file:\s+(\S+)$/) { - push(@files, $1) - if (-f $1); - } -} -close(CMD); +my @files = find_modified_files($vcs); if ($#files < 0) { - print "No added / changed files -- nothing to do\n"; + quiet_print "No added / changed files -- nothing to do\n"; exit(0); } # Examine each of the files and see if they need an updated copyright foreach my $f (@files) { - print "Processing added/changed file: $f\n"; + quiet_print "Processing added/changed file: $f\n"; open(FILE, $f) || die "Can't open file: $f"; # Read in the file, and look for the "$COPYRIGHT$" token; that's @@ -159,8 +174,8 @@ foreach my $f (@files) { # If there was not copyright token, don't do anything if (!defined($token_line_index)) { - print "==> WARNING: Did not find the \$COPYRIGHT\$ token!\n"; - print " File left unchanged\n"; + quiet_print "==> WARNING: Did not find the \$COPYRIGHT\$ token!\n"; + quiet_print " File left unchanged\n"; next; } @@ -170,13 +185,13 @@ foreach my $f (@files) { # Now act on it if (!defined($my_line_index)) { - print "--- My copyright line not found; adding:\n"; + quiet_print "--- My copyright line not found; adding:\n"; my $str = "${prefix}Copyright (c) $year $my_formal_name\n"; - print " $str"; + quiet_print " $str"; $lines[$token_line_index] = $str . $lines[$token_line_index]; } else { - print "--- Found existing copyright line:\n"; - print " $lines[$my_line_index]"; + quiet_print "--- Found existing copyright line:\n"; + quiet_print " $lines[$my_line_index]"; $lines[$my_line_index] =~ m/([\d+\-]+)/; my $years = $1; die "Could not find years in copyright line!" @@ -202,10 +217,10 @@ foreach my $f (@files) { # Do we need to do anything? if ($year > $last_year) { $lines[$my_line_index] = "${prefix}Copyright (c) $first_year-$year $my_formal_name\n"; - print " Updated to:\n"; - print " $lines[$my_line_index]"; + quiet_print " Updated to:\n"; + quiet_print " $lines[$my_line_index]"; } else { - print " This year already included in copyright; not changing file\n"; + quiet_print " This year already included in copyright; not changing file\n"; next; } } @@ -217,7 +232,115 @@ foreach my $f (@files) { print FILE join('', @lines); close(FILE); - # Now replace the old one - unlink($f); - rename($newf, $f); + if ($CHECK_ONLY) { + # intentional "loud" print to be more useful in a pre-commit hook + print "==> '$f' has a stale/missing copyright\n"; + unlink($newf); + ++$would_replace; + } + else { + # Now replace the old one + unlink($f); + rename($newf, $f); + } +} + +if ($CHECK_ONLY and $would_replace) { + exit(111); +} + +#------------------------------------------------------------------------------- + +# Takes two arguments, the top level directory and the VCS method. Returns a +# list of file names (relative to pwd) which the VCS considers to be modified. +sub find_modified_files { + my $vcs = shift; + my @files = (); + + if ($vcs eq "git") { + # Number of path entries to remove from ${top}-relative paths. + # (--show-cdup either returns the empty string or sequence of "../" + # entries, always ending in a "/") + my $n_strip = scalar(split(m!/!, scalar(`git rev-parse --show-cdup`))) - 1; + + # "." restricts scope, but does not get us relative path names + my $cmd = "git status -z --porcelain --untracked-files=no ."; + quiet_print "==> Running: \"$cmd\"\n"; + my $lines = `$cmd`; + + # From git-status(1): + # X Y Meaning + # ------------------------------------------------- + # [MD] not updated + # M [ MD] updated in index + # A [ MD] added to index + # D [ M] deleted from index + # R [ MD] renamed in index + # C [ MD] copied in index + # [MARC] index and work tree matches + # [ MARC] M work tree changed since index + # [ MARC] D deleted in work tree + # ------------------------------------------------- + # D D unmerged, both deleted + # A U unmerged, added by us + # U D unmerged, deleted by them + # U A unmerged, added by them + # D U unmerged, deleted by us + # A A unmerged, both added + # U U unmerged, both modified + # ------------------------------------------------- + # ? ? untracked + # ------------------------------------------------- + foreach my $line (split /\x{00}/, $lines) { + my $keep = 0; + my ($s1, $s2, $fullname) = $line =~ m/^(.)(.) (.*)$/; + + # ignore all merge cases + next if ($s1 eq "D" and $s2 eq "D"); + next if ($s1 eq "A" and $s2 eq "A"); + next if ($s1 eq "U" or $s2 eq "U"); + + # only update for actually added/modified cases, no copies, + # renames, etc. + $keep = 1 if ($s1 eq "M" or $s2 eq "M"); + $keep = 1 if ($s1 eq "A"); + + if ($keep) { + my $relname = $fullname; + $relname =~ s!^([^/]*/){$n_strip}!!g; + + push @files, $relname + if (-f $relname); + } + } + } + elsif ($vcs eq "hg" or $vcs eq "svn") { + my $cmd = "$vcs st ."; + + # Run the command, parsing the output. Make a list of files that are + # added or modified. + quiet_print "==> Running: \"$cmd\"\n"; + open(CMD, "$cmd|") || die "Can't run command"; + while () { + chomp; + if ($_ =~ /^M/ || $_ =~ /^A/) { + my @tokens = split(/\s+/, $_); + # Handle output of both forms: + # M filenameA + # A + filenameB + my $filename = $tokens[1]; + $filename = $tokens[2] + if ($tokens[1] =~ /\+/); + # Don't bother saving directory names + push(@files, $filename) + if (-f $filename); + } + } + close(CMD); + } + else { + die "unknown VCS '$vcs', stopped"; + } + + return @files; }