123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- # ------------------------------------------------------------------------------
- # NAME
- # Fcm::CmCommitMessage
- #
- # DESCRIPTION
- # This class contains methods to read, write and edit the commit message file
- # in a working copy.
- #
- # COPYRIGHT
- # (C) Crown copyright Met Office. All rights reserved.
- # For further details please refer to the file COPYRIGHT.txt
- # which you should have received as part of this distribution.
- # ------------------------------------------------------------------------------
- package Fcm::CmCommitMessage;
- @ISA = qw(Fcm::Base);
- # Standard pragma
- use warnings;
- use strict;
- # Standard modules
- use Carp;
- use Cwd;
- use File::Spec;
- use File::Temp qw/tempfile/;
- # FCM component modules
- use Fcm::Base;
- use Fcm::Util qw/e_report run_command/;
- # List of property methods for this class
- my @scalar_properties = (
- 'auto_mesg', # the automatically inserted part of a commit message
- 'base', # the base name of the commit message file
- 'dir', # the directory container of the commit message file
- 'ignore_mesg', # the ignored part of a commit message
- 'user_mesg', # the user defined part of a commit message
- );
- # Commit log delimiter messages
- my $log_delimiter = '--Add your commit message ABOVE - ' .
- 'do not alter this line or those below--';
- my $auto_delimiter = '--FCM message (will be inserted automatically)--';
- my $auto_delimiter_old = '--This line will be ignored and those below ' .
- 'will be inserted automatically--';
- my $status_delimiter = '--Change summary ' .
- '(not part of commit message)--';
- my $status_delimiter_old = '--This line, and those below, will be ignored--';
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $obj = Fcm::CmCommitMessage->new ();
- #
- # DESCRIPTION
- # This method constructs a new instance of the Fcm::CmCommitMessage class.
- # ------------------------------------------------------------------------------
- sub new {
- my $this = shift;
- my %args = @_;
- my $class = ref $this || $this;
- my $self = Fcm::Base->new (%args);
- $self->{$_} = undef for (@scalar_properties);
- bless $self, $class;
- return $self;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $value = $obj->X;
- # $obj->X ($value);
- #
- # DESCRIPTION
- # Details of these properties are explained in @scalar_properties.
- # ------------------------------------------------------------------------------
- for my $name (@scalar_properties) {
- no strict 'refs';
- *$name = sub {
- my $self = shift;
- # Argument specified, set property to specified argument
- if (@_) {
- $self->{$name} = $_[0];
- }
- # Default value for property
- if (not defined $self->{$name}) {
- if ($name eq 'base') {
- # Reference to an array
- $self->{$name} = '#commit_message#';
- } elsif ($name eq 'dir') {
- # Current working directory
- $self->{$name} = &cwd ();
- } elsif ($name =~ /_mesg$/) {
- # Reference to an array
- $self->{$name} = [];
- }
- }
- return $self->{$name};
- }
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $file = $obj->file;
- # $obj->file ($file);
- #
- # DESCRIPTION
- # This method returns the full name of the commit message file. If an
- # argument is specified, the file is reset using the value of the argument.
- # ------------------------------------------------------------------------------
- sub file {
- my ($self, $file) = @_;
- if ($file) {
- $self->dir (dirname ($file));
- $self->base (basename ($file));
- }
- return File::Spec->catfile ($self->dir, $self->base);
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # ($user, $auto) = $obj->read_file ();
- #
- # DESCRIPTION
- # This function reads from the commit log message file. It resets the user
- # and the automatic messages after reading the file. It returns the message
- # back in two array references.
- # ------------------------------------------------------------------------------
- sub read_file {
- my $self = shift;
- my @user = ();
- my @auto = ();
- my $file = $self->file;
- if (-r $file) {
- open FILE, '<', $file or croak 'Cannot open ', $file, '(', $!, '), abort';
- my $in_auto = 0;
- while (<FILE>) {
- next if (index ($_, $log_delimiter) == 0);
- if (index ($_, $status_delimiter) == 0 ||
- index ($_, $status_delimiter_old) == 0) {
- # Ignore after the ignore delimiter
- last;
- }
- if (index ($_, $auto_delimiter) == 0 ||
- index ($_, $auto_delimiter_old) == 0) {
- # Beginning of the automatically inserted message
- $in_auto = 1;
- next;
- }
- if ($in_auto) {
- push @auto, $_;
- } else {
- push @user, $_;
- }
- }
- close FILE;
- $self->user_mesg (\@user);
- $self->auto_mesg (\@auto);
- }
- return (\@user, \@auto);
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $obj->write_file ();
- #
- # DESCRIPTION
- # This function writes to the commit log message file based on the content of
- # the user defined message, and the automatically inserted message.
- # ------------------------------------------------------------------------------
- sub write_file {
- my $self = shift;
- my %args = @_;
- my @user = @{ $self->user_mesg };
- my @auto = @{ $self->auto_mesg };
- my $file = $self->file;
- open FILE, '>', $file or die 'Cannot open ', $file, '(', $!, '), abort';
- print FILE @user;
- print FILE $log_delimiter, "\n", $auto_delimiter, "\n", @auto if @auto;
- close FILE or croak 'Cannot close ', $file, '(', $!, '), abort';
- return;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $file = $obj->edit_file ([TEMP => 1,] [BATCH => 1,]);
- #
- # DESCRIPTION
- # This function normally triggers an editor for editing the commit message.
- # If TEMP is set, it edits a temporary file. Otherwise, it edits the current
- # commit message file. It resets the user defined message on success. Returns
- # the name of the commit log file. Do not start the editor if BATCH is set.
- # ------------------------------------------------------------------------------
- sub edit_file {
- my $self = shift;
- my %args = @_;
- my $temp = exists $args{TEMP} ? $args{TEMP} : 0;
- my $batch = exists $args{BATCH} ? $args{BATCH} : 0;
- my @user = @{ $self->user_mesg };
- my @auto = @{ $self->auto_mesg };
- my @ignore = @{ $self->ignore_mesg };
- my $file = $self->file;
- if ($temp) {
- my $fh;
- ($fh, $file) = tempfile (SUFFIX => ".fcm", UNLINK => 1);
- close $fh;
- }
- # Add original or code driven message and status information to the file
- my $select = select;
- open FILE, '>', $file or croak 'Cannot open ', $file, ' (', $!, '), abort';
- select FILE;
- print @user;
- print (@auto || @user ? '' : "\n");
- print $log_delimiter, "\n";
- print $auto_delimiter, "\n", @auto, "\n" if @auto;
- print $status_delimiter, "\n\n";
- print @ignore if @ignore;
- close FILE or die 'Cannot close ', $file, ' (', $!, '), abort';
- select $select;
- if (not $batch) {
- # Select editor
- my $editor = 'nedit';
-
- if ($ENV{'SVN_EDITOR'}) {
- $editor = $ENV{'SVN_EDITOR'};
- } elsif ($ENV{'VISUAL'}) {
- $editor = $ENV{'VISUAL'};
- } elsif ($ENV{'EDITOR'}) {
- $editor = $ENV{'EDITOR'};
- }
- # Execute command to start the editor
- print 'Starting ', $editor, ' to edit commit message ...', "\n";
- &run_command ([split (/\s+/, $editor), $file]);
- }
- # Read the edited file, and extract user log message from it
- open FILE, '<', $file or croak 'Cannot open ', $file, ' (', $!, '), abort';
- my (@log);
- my $delimiter_found = 0;
- while (<FILE>) {
- if (index ($_, $log_delimiter) == 0) {
- $delimiter_found = 1;
- last;
- }
- push @log, $_;
- }
- close FILE;
- # Ensure log delimiter line was not altered
- e_report 'Error: the line "', $log_delimiter, '" has been altered, abort.'
- if not $delimiter_found;
- # Check for empty commit log
- e_report 'Error: log message unchanged or not specified, abort.'
- if join (' ', (@log, @auto)) =~ /^\s*$/;
- # Echo the commit message to standard output
- my $separator = '-' x 80 . "\n";
- print 'Change summary:', "\n";
- print $separator, @ignore, $separator;
- print 'Commit message is as follows:', "\n";
- print $separator, @log, @auto, $separator;
- open FILE, '>', $file or croak 'Cannot open ', $file, ' (', $!, '), abort';
- print FILE @log, @auto;
- close FILE or croak 'Cannot close ', $file, ' (', $!, '), abort';
- # Reset the array for the user specified log message
- $self->user_mesg (\@log);
- return $file;
- }
- # ------------------------------------------------------------------------------
- 1;
- __END__
|