#!/usr/bin/perl
#
# echangelog: Update the ChangeLog for an ebuild.  For example:
#
#   $ echangelog 'Add ~alpha to KEYWORDS'
#   4a5,7
#   >   10 Feb 2003; Aron Griffis <agriffis@gentoo.org> oaf-0.6.8-r1.ebuild :
#   >   Add ~alpha to KEYWORDS
#   >

use warnings;
use strict;
use POSIX qw(locale_h strftime getcwd setlocale);
use File::Basename;
use Getopt::Long;

# Fix bug 21022 by restricting to C locale
setlocale(LC_ALL, "C");

use Text::Wrap;
$Text::Wrap::columns = 80;
$Text::Wrap::unexpand = 0;

# Global variables
my (@files, @ebuilds, @conflicts, @trivial, @unknown, @new_versions, %actions);
my ($input, $editor, $entry, $user, $date, $text, $vcs);
my ($fh);
my ($opt_help, $opt_nostrict, $opt_force_vcs, $opt_version, $opt_strict);

$date = strftime("%d %b %Y", gmtime);

$opt_help = 0;
$opt_nostrict = 0;
$opt_version = 0;
# DEPRECATED
$opt_strict = 0;

my %vcs = (
	bzr => {
		directory => ".bzr",
		diff => "bzr diff",
		status => "bzr status -S .",
		add => "bzr add",
		skip => 3,
		# The same as for hg.
		regex => qr/^=== \S+ file '\S+\/\S+\/((\S+)\.ebuild)/
	},
	cvs => {
		directory => "CVS",
		diff => "cvs -f diff -U0",
		status => "cvs -fn up",
		add => "cvs -f add",
		skip => 6,
		regex => qr/^Index: (([^\/]*?)\.ebuild)\s*$/
	},
	git => {
		directory => ".git",
		diff => "git diff",
		status => "git diff-index HEAD --name-status",
		add => "git add",
		# This value should usually be 3 but on new file mode we need skip+1.
		# So 4 should be fine anyway.
		skip => 4,
		regex => qr/^diff \-\-git \S*\/((\S*)\.ebuild)/
	},
	hg => {
		directory => ".hg",
		diff => "hg diff",
		status => "hg status .",
		add => "hg add",
		skip => 3,
		# hg diff is relative to the root.
		# TODO: Write a proper regex :)
		regex => qr/diff \-r \S+ \S+\/\S+\/((\S+)\.ebuild)/
	},
	svn => {
		directory => ".svn",
		diff => "svn diff -N",
		status => "svn status",
		add => "svn add",
		skip => 4,
		regex => qr/^Index: (([^\/]*?)\.ebuild)\s*$/
	},
);

sub usage {
	(my $usage = <<"	EOF") =~ s/^\t//gm;
	Usage: echangelog [options] <changelog message>

	Options:
	    --help      err, this screen ...
	    --no-strict do not abort on trivial/no changes
	    --vcs <vcs> skip vcs autodetection and use the specified vcs instead
	                for a list of supported version control systems see below
	    --version   show version info

	Supported VCS: bzr, cvs, git, hg, svn
	EOF
	print $usage;
	exit 0;
}

sub version {
	my $Revision = "Last svn change rev";
	my $Date = "Last svn change date";
	my $foo = "";
	print "echangelog\n$Revision$foo \n$Date$foo\n";
	exit 0;
}

sub getenv($) {
    my $key = shift;

    # Ensure our variable exist
    if ( defined($ENV{$key}) ) {
        # Ensure we don't get empty variables
        if ( length($ENV{$key}) > 0 ) {
            return $ENV{$key};
        }
    }
    return;
}

# Bug 264146.
# Copied from Text::Wrap.
# The only modified thing is:
# We trim _just_ tab/space etc. but not \n/\r.
# \s treats even \n/\r as whitespace.
# BUGS:
# ' test'
# ' test'
# Will end up in:
# ' test'
# ''
# 'test'
# See 'my $ps = ($ip eq $xp) ? "\n\n" : "\n";'
sub text_fill {
	my ($ip, $xp, @raw) = @_;
	my @para;

	for my $pp ( split(/\n\s+/, join("\n", @raw)) ) {
		$pp =~ s/[\x09\x0B\x0C\x20]+/ /g;
		my $x = Text::Wrap::wrap($ip, $xp, $pp);
		push(@para, $x);
	}

	# if paragraph_indent is the same as line_indent,
	# separate paragraphs with blank lines
	my $ps = ($ip eq $xp) ? "\n\n" : "\n";
	return join ($ps, @para);
}

sub changelog_info {
	my %changed = @_;

	open(my $fh, '>', 'ChangeLog.new');

	print($fh "\n");
	print($fh "# Please enter the ChangeLog message for your changes. Lines starting\n");
	print($fh "# with '#' will be ignored, and an empty message aborts the ChangeLog.\n");
	print($fh "#\n# Changes:\n");

	foreach my $key (keys(%changed)) {
		if ($changed{$key} eq "+") {
			printf($fh "# new file:\t%s\n", $key);
		}
		elsif ($changed{$key} eq "-") {
			printf($fh "# deleted:\t%s\n", $key);
		}
		else {
			printf($fh "# modified:\t%s\n", $key);
		}
	}

	close($fh);
}

sub update_cat_pn {
	my $t = shift;
	my $cwd = getcwd();

	my $category = basename(dirname($cwd));
	my $package_name = basename($cwd);

	$t =~ s/^(# ChangeLog for).*/$1 $category\/$package_name/;

	return $t;
}

# Check partent dirs recursivevly/backward
sub check_vcs_dir {
	my $type = shift;

	my $dir = getcwd();
	while($dir !~ /^\/$/) {
		return 1 if -d "${dir}/${type}";
		$dir = dirname($dir);
	}
	# Check / as well
	return 1 if -d "/${type}";

	return 0;
}

# Just to ensure we don't get duplicate entries.
sub mypush(\@@) {
	my $aref = shift;

	foreach my $value (@_) {
		push(@{$aref}, $value) if !grep(/^\Q$value\E$/, @{$aref});
	}
}

sub update_copyright {
	my ($t) = @_;
	(my $year = $date) =~ s/.* //;

	$t =~ s/^# Copyright \d+(?= )/$&-$year/m or
	$t =~ s/^(# Copyright) (\d+)-\d+/$1 $2-$year/m;

	return $t;
}

GetOptions(
	'help' => \$opt_help,
	'no-strict' => \$opt_nostrict,
	'vcs=s' => \$opt_force_vcs,
	'version|v' => \$opt_version,
	'strict' => \$opt_strict,
);

usage() if $opt_help;
version() if $opt_version;

if($opt_strict) {
	print STDERR "Warning: The option '--strict' has been deprecated and will be removed soon!\n";
	print STDERR "--strict behaviour is now default.\n";
}

# Figure out what kind of repo we are in.
# Respect $PATH while looking for the VCS
if(! defined($opt_force_vcs)) {
	foreach my $path ( split(":", (getenv("PATH") || "/bin:/usr/bin:/usr/local/bin")) ) {
		foreach my $_vcs (sort(keys(%vcs))) {
			if ( -X "${path}/${_vcs}" ) {
				$vcs = $_vcs if check_vcs_dir($vcs{$_vcs}{directory});
				last if $vcs;
			}
		}
		last if $vcs;
	}
}
else {
	$vcs = $opt_force_vcs if defined $vcs{$opt_force_vcs};
}

if ( ! $vcs ) {
	print STDERR "Either no CVS, .git, .svn, ... directories found, the specific VCS has not been\n";
	print STDERR "installed or you don't have execute rights!\n";
	exit(1);
}

# Read the current ChangeLog
if (-f 'ChangeLog') {
	open($fh, '<', 'ChangeLog') or die "Can't open ChangeLog for input: $!\n";
	{ local $/ = undef; $text = <$fh>; }
	close($fh);
} else {
	# No ChangeLog here, maybe we should make one...
	if (glob("*.ebuild")) {
		open(my $ph, '-|', "portageq portdir") or die "portageq returned with an error: $!\n";
		my $portdir = <$ph>;
		$portdir =~ s/\s+$//;
		close($ph);

		die "Can't find PORTDIR\n" if (length $portdir == 0);

		open($fh, '<', "$portdir/skel.ChangeLog")
			or die "Can't open $portdir/skel.ChangeLog for input: $!\n";
		{ local $/ = undef; $text = <$fh>; }
		close($fh);

		$text =~ s/^\*.*//ms; # don't need the fake entry
	} else {
		die "This should be run in a directory with ebuilds...\n";
	}
}

# Update the copyright year in the ChangeLog
$text = update_copyright($text);

# New packages and/or ones that have moved around often have stale data here.
# But only do that in places where ebuilds are around (as echangelog can be
# used in profiles/ and such places).
if (glob("*.ebuild")) {
	$text = update_cat_pn($text);
}

# Figure out what has changed around here
open(my $ph_status, "-|", $vcs{$vcs}{status}.' 2>&1') or die "Can't run ".$vcs{$vcs}{status}.": $!\n";
while (<$ph_status>) {
	# I don't want mess our existing stuff with the horrible bazaar stuff.
	# TODO: add stuff for untracked/conflicting files.
	if ($vcs eq "bzr") {
		# NEW, DELETED, MODIFIED
		if (/^[\s\+\-]([NDM])\s+(.*)/) {
			my ($status, $filename) = ($1, $2);
			# strip category/package/ since everything is relative to the repo root.
			$filename =~ s/^([^\/]+\/){2}//;

			# skip empty $filename, e.g. if you add a new package, the first
			# line would be the package directory app-foo/bar/ but thats stripped above.
			next if !$filename;
			# skip directories
			next if -d $filename;

			($actions{$filename} = $status) =~ tr/NDM/+-/d;
			push(@files, $filename);
			next;
			}
		# RENAMED/MOVED/MODIFIED
		elsif (/^RM?\s+(\S+) => (\S+)/) {
			my ($old, $new) = ($1, $2);
			$old =~ s/^([^\/]+\/){2}//;
			$new =~ s/^([^\/]+\/){2}//;

			next if !$old or !$new;
			next if -d $old or -d $new;

			$actions{$old} = '-';
			$actions{$new} = '+';

			push(@files, $old, $new);
			next;
		}
	}
	if (/^C\s+(\S+)/) {
		# NOTE: The git part here might be unused
		if($vcs eq "git") {
			my $filename = $1;
			$filename =~ /\S*\/(\S*)/;

			next if -d $filename;

			push @conflicts, $filename;
			next;
		}

		push @conflicts, $1;
		next;
	}
	elsif (/^\?\s+(\S+)/) {
		push @unknown, $1;
		$actions{$1} = '?';
		next;
	}
	elsif (/^([ARMD])\s+\+?\s*(\S+)/) {
		my ($status, $filename) = ($1,$2);

		if($vcs eq "git") {
			open(my $ph, '-|', "git rev-parse --sq --show-prefix");
			my $prefix = <$ph>;
			close($ph);

			if (defined($prefix)) {
				chomp($prefix);

				if ($filename =~ /\Q$prefix\E(\S*)/) {
					$filename = $1 ;
				}
				else {
					next;
				}
			}
		}

		next if -d $filename;

		push(@files, $filename);
		($actions{$filename} = $status) =~ tr/DARM/-+-/d;
	}
}
close($ph_status);

sub git_unknown_objects {
	open(my $ph, "-|", "${vcs} ls-files --exclude-standard --others");
	while(defined( my $line = <$ph> )) {
		chomp($line);

		# IMHO we can skip those files, even if they're untracked
		#next if $line =~ m/^\.gitignore$/;

		push(@unknown, $line);
	}
	close($ph);
}

# git only shows files already added so we need to check for unknown files
# separately here.
if($vcs eq "git") {
	git_unknown_objects();
}

# Separate out the trivial files for now
@files = grep {
	!/^(Manifest|ChangeLog)$/ or do { push @trivial, $_; 0; }
} @files;

@unknown = grep {
	!/^(Manifest|ChangeLog)$/ or do { push @trivial, $_; 0; }
} @unknown;

# Don't allow any conflicts
if (@conflicts) {
	print STDERR <<EOT;
$vcs reports the following conflicts.  Please resolve them before
running echangelog.
EOT
	print STDERR map "C $_\n", @conflicts;
	exit 1;
}

# Don't allow unknown files (other than the trivial files that were separated
# out above)
if (@unknown) {
	print STDERR <<EOT;
$vcs reports the following unknown files.  Please use "$vcs add" before
running echangelog, or remove the files in question.
EOT
	print STDERR map "? $_\n", @unknown;
	exit 1;
}

# Sort the list of files as portage does.  None of the operations through
# the rest of the script should break this sort.
sub sortfunc($$) {
	my ($a, $b) = @_;
	(my $va = $a) =~ s/.*?-(\d.*?)(?:\.ebuild)?$/$1/;
	(my $vb = $b) =~ s/.*?-(\d.*?)(?:\.ebuild)?$/$1/;
	my ($na, $sa, $sna, $ra) = ($va =~ /^(.*?)(?:_(alpha|beta||pre|rc|p)(\d*))?(?:-r(\d+))?$/);
	my ($nb, $sb, $snb, $rb) = ($vb =~ /^(.*?)(?:_(alpha|beta||pre|rc|p)(\d*))?(?:-r(\d+))?$/);
	my (@na) = split /\.|(?<=\d)(?=[^\d\.])/, $na;
	my (@nb) = split /\.|(?<=\d)(?=[^\d\.])/, $nb;
	my $retval;

	#
	# compare version numbers first
	#
	for (my $i = 0; defined $na[$i] or defined $nb[$i]; $i++) {
		# def vs. undef
		return +1 if defined $na[$i] and !defined $nb[$i];
		return -1 if defined $nb[$i] and !defined $na[$i];

		# num vs. num
		if ($na[$i] =~ /^\d/ and $nb[$i] =~ /^\d/) {
			$retval = ($na[$i] <=> $nb[$i]);
			return $retval if $retval;
			next;
		}

		# char vs. char
		if ($na[$i] =~ /^\D/ and $nb[$i] =~ /^\D/) {
			$retval = ($na[$i] cmp $nb[$i]);
			return $retval if $retval;
			next;
		}

		# num vs. char
		$retval = ($na[$i] =~ /\d/ and -1 or +1);
		return $retval;
	}

	#
	# compare suffix second
	#
	if (defined $sa and !defined $sb) {
		return +2 if $sa eq "p";
		return -2;
	}
	if (defined $sb and !defined $sa) {
		return -3 if $sb eq "p";
		return +3;
	}

	if (defined $sa) { # and defined $sb
		$retval = ($sa cmp $sb);
		if ($retval) {
			return +4 if $sa eq "p";
			return -4 if $sb eq "p";
			return $retval; # suffixes happen to be alphabetical order, mostly
		}

		# compare suffix number
		return +5 if defined $sna and !defined $snb;
		return -5 if defined $snb and !defined $sna;

		if (defined $sna) {  # and defined $snb
			$retval = ($sna <=> $snb);
			return $retval if $retval;
		}
	}

	#
	# compare rev third
	#
	return +6 if defined $ra and !defined $rb;
	return -6 if defined $rb and !defined $ra;

	if (defined $ra) { # and defined $rb
		return ($ra <=> $rb);
	}

	#
	# nothing left to compare
	#
	return 0;
}

# Forget ebuilds that only have changed copyrights, unless that's all
# the changed files we have
@ebuilds = grep(/\.ebuild$/, @files);
@files = grep(!/\.ebuild$/, @files);

if (@ebuilds) {
	my $ph_diff;
	if ($vcs eq "git") {
		open($ph_diff, "-|", $vcs{$vcs}{diff}." HEAD -- @ebuilds 2>&1") or die "Can't run: ".$vcs{$vcs}{diff}."$!\n";
	} else {
		open($ph_diff, "-|", $vcs{$vcs}{diff}." @ebuilds 2>&1") or die "Can't run: ".$vcs{$vcs}{diff}."$!\n";
	}

	while (defined(my $line = <$ph_diff>)) {
		# only possible with cvs
		if ($line =~ m/^$vcs diff: (([^\/]*?)\.ebuild) was removed/) {
			mypush(@ebuilds, $1);
		}
		# We assume GNU diff output format here.
		# git format: diff --git a/app-doc/repodoc/metadata.xml b/app-doc/repodoc/metadata.xml
		elsif ($line =~ m/$vcs{$vcs}{regex}/) {
			my ($file, $version) = ($1, $2);

			if ($vcs eq "git") {
				while (defined($line = <$ph_diff>)) {
					last if $line =~ m/^deleted file mode|^index/;
					if ($line =~ m/^new file mode/) {
						mypush(@ebuilds, $file);
						mypush(@new_versions, $version);
						last;
					}
				}
			}

			if ($vcs eq "bzr") {
				if ($line =~ m/^=== added file/) {
					mypush(@ebuilds, $file);
					mypush(@new_versions, $version);
					last;
				}
				elsif($line =~ /^=== renamed file '.+\/([^\/]+\.ebuild)' => '.+\/(([^\/]+)\.ebuild)'/) {
					mypush(@ebuilds, $1, $2);
					mypush(@new_versions, $3);
					last;
				}
			}

			# check if more than just copyright date changed.
			# skip some lines (vcs dependent)
			foreach(1..$vcs{$vcs}{skip}) {
				$line = <$ph_diff>;
			}


			my $copy_only = 1;
			while(defined($line = <$ph_diff>)) {
				# We just want to check/compare the differences so anything beginning with +/-
				if ($line =~ m/^[-+](?!# Copyright)/m) {
					mypush(@ebuilds, $file);
					$copy_only = 0;
					last;
				}
			}

			# Only the Copyright has been changed so lets remove the file from the array
			if ($copy_only) {
				@ebuilds = grep(!/\Q${file}\E/, @ebuilds);
			}

			# At this point all ebuilds where more than just the copyright has been changed have been added to @ebuilds.
			# So lets go ahead with the next diff.
			next;
		}
		elsif ($line =~ m/^$vcs.*?: (([^\/]*?)\.ebuild) is a new entry/) {
			mypush(@ebuilds, $1);
			mypush(@new_versions, $2);
		}
	}
	close($ph_diff);
}

# Subversion diff doesn't identify new versions. So use the status command
if (($vcs eq "svn" or $vcs eq "hg") and (@ebuilds)) {
	open(my $ph_status, "-|", $vcs{$vcs}{status}." @ebuilds 2>&1") or die "Can't run: ".$vcs{$vcs}{status}."$!\n";

	while (defined(my $line = <$ph_status>)) {
		if ($line =~ m/^A\s+\+?\s*(([^\s]*)\.ebuild)/) {
			mypush(@ebuilds, $1);
			mypush(@new_versions, $2);
		}
	}
	close($ph_status);
}

# When a package move occurs, the versions appear to be new even though they are
# not.  Trim them from @new_versions in that case.
@new_versions = grep { $text !~ /^\*\Q$_\E\s/m } @new_versions;

# Allow ChangeLog entries with no changed files, but give a fat warning
if (!@files && !@ebuilds) {
	print STDERR "**\n";
	print STDERR "** NOTE: No non-trivial changed files found. Normally echangelog\n";
	print STDERR "** should be run after all affected files have been added and/or\n";
	print STDERR "** modified.  Did you forget to $vcs add?\n";
	print STDERR "**\n";

	if (!$opt_nostrict) {
		print STDERR "** In strict mode, exiting\n";
		print STDERR "** If you know what you're doing there pass '--no-strict' to echangelog\n";
		exit(1);
	}

	@files = @trivial;

	# last resort to put something in the list
	unless (@files) {
		@files = qw/ChangeLog/;
		$actions{'ChangeLog'} = "";
	}
}

# sort
@ebuilds = sort sortfunc @ebuilds;
@files = sort sortfunc @files;
@new_versions = sort sortfunc @new_versions;

# Get the input from the cmdline, editor or stdin
if ($ARGV[0]) {
	$input = "@ARGV";
} else {
	$editor = getenv('ECHANGELOG_EDITOR') ? getenv('ECHANGELOG_EDITOR') : getenv('EDITOR') || undef;

	if ($editor) {
		# Append some informations.
		changelog_info(%actions);

		system("$editor ChangeLog.new");

		if ($? != 0) {
			# This usually happens when the editor got forcefully killed; and
			# the terminal is probably messed up: so we reset things.
			system('stty sane');
			print STDERR "Editor died!  Reverting to stdin method.\n";
			undef $editor;
		} else {
			if (open($fh, "<", "ChangeLog.new")) {
				local $/ = undef;
				$input = <$fh>;
				close($fh);

				# Remove comments from changelog_info().
				local $/ = "\n";
				$input =~ s/^#.*//mg;
				local $/ = undef;
			} else {
				print STDERR "Error opening ChangeLog.new: $!\n";
				print STDERR "Reverting to stdin method.\n";
				undef $editor;
			}
		}
		unlink('ChangeLog.new') if -f 'ChangeLog.new';
	}

	unless ($editor) {
		print "Please type the log entry: use Ctrl-d to finish, Ctrl-c to abort...\n";
		local $/ = undef;
		$input = <>;
	}
}
die "Empty entry; aborting\n" unless $input =~ /\S/;

# If there are any long lines, then wrap the input at $columns chars
# (leaving 2 chars on left, one char on right, after adding indentation below).
$input = text_fill('  ', '  ', $input);

# Prepend the user info to the input
# Changes related to bug 213374;
# This sequence should be right:
# 1. GENTOO_COMMITTER_NAME && GENTOO_COMMITTER_EMAIL
# 2. GENTOO_AUTHOR_NAME && GENTOO_AUTHOR_EMAIL
# 3. ECHANGELOG_USER (fallback/obsolete?)
# 4. getpwuid()..
if ( getenv("GENTOO_COMMITTER_NAME") && getenv("GENTOO_COMMITTER_EMAIL") ) {
	$user = sprintf("%s <%s>", getenv("GENTOO_COMMITTER_NAME"), getenv("GENTOO_COMMITTER_EMAIL"));
}
elsif ( getenv("GENTOO_AUTHOR_NAME") && getenv("GENTOO_AUTHOR_EMAIL") ) {
	$user = sprintf("%s <%s>", getenv("GENTOO_AUTHOR_NAME"), getenv("GENTOO_AUTHOR_EMAIL"));
}
elsif ( getenv("ECHANGELOG_USER") ) {
	$user = getenv("ECHANGELOG_USER");
}
else {
	my ($fullname, $username) = (getpwuid($<))[6,0];
	$fullname =~ s/,.*//; # remove GECOS, bug 80011
	$user = sprintf('%s <%s@gentoo.org>', $fullname, $username);
}

# Make sure that we didn't get "root"
die "Please set ECHANGELOG_USER or run as non-root\n" if $user =~ /<root@/;

$entry = "$date; $user ";
if(@ebuilds) {
	$entry .= join(', ', map("$actions{$_}$_", @ebuilds));
	$entry .= ", ".join(', ', map("$actions{$_}$_", @files)) if @files;
}
else {
	$entry .= join ', ', map("$actions{$_}$_", @files);
}
$entry .= ':';
$entry = Text::Wrap::fill('  ', '  ', $entry); # does not append a \n
$entry .= "\n$input";                          # append user input

# Each one of these regular expressions will eat the whitespace
# leading up to the next entry (except the two-space leader on the
# front of a dated entry), so it needs to be replaced with a
# double carriage-return.  This helps to normalize the spacing in
# the ChangeLogs.
if (@new_versions) {
	# Insert at the top with a new version marker
	$text =~ s/^( .*? )               # grab header
		\s*\n(?=\ \ \d|\*|\z)  # suck up trailing whitespace
		/"$1\n\n" .
		join("\n", map "*$_ ($date)", reverse @new_versions) .
		"\n\n$entry\n\n"/sxe
			or die "Failed to insert new entry (4)\n";
} else {
	# Changing an existing patch or ebuild, no new version marker
	# required
	$text =~ s/^( .*? )               # grab header
		\s*\n(?=\ \ \d|\*|\z)  # suck up trailing whitespace
		/$1\n\n$entry\n\n/sx
			or die "Failed to insert new entry (3)\n";
}

# Update affected ebuilds and some other files copyright dates.  There is no reason to update the
# copyright lines on ebuilds that haven't changed. I verified this with an IP
# lawyer. (patches/diffs and binary files are excluded)
for my $e (grep(!/\.(patch|diff)$/, @files), @ebuilds) {
	if (-s $e && ! -B $e) {
		my ($etext, $netext);

		open($fh, "<", $e) or warn("Can't read $e to update copyright year\n"), next;
		{ local $/ = undef; $etext = <$fh>; }
		close($fh);

		# Attempt the substitution and compare
		$netext = update_copyright($etext);
		next if $netext eq $etext; # skip this file if no change.

		# Write the new ebuild
		open($fh, ">", "${e}.new") or warn("Can't open $e.new\n"), next;
		print $fh $netext and
		close($fh) or warn("Can't write $e.new\n"), next;

		# Move things around and show the diff
		system "diff -U 0 $e $e.new";
		rename "$e.new", $e or warn("Can't rename $e.new: $!\n");

		# git requires to re-add this file else it wouln't be included in the commit.
		if ($vcs eq "git") {
			system("$vcs{$vcs}{add} ${e}");
		}
	}
}

# Write the new ChangeLog
open($fh, ">", 'ChangeLog.new') or die "Can't open ChangeLog.new for output: $!\n";
print $fh $text or die "Can't write ChangeLog.new: $!\n";
close($fh) or die "Can't close ChangeLog.new: $!\n";

# Move things around and show the ChangeLog diff
system 'diff -Nu ChangeLog ChangeLog.new';
rename 'ChangeLog.new', 'ChangeLog' or die "Can't rename ChangeLog.new: $!\n";

# Okay, now we have a starter ChangeLog to work with.
# The text will be added just like with any other ChangeLog below.
# Add the new ChangeLog to vcs before continuing.
if ($vcs eq "cvs") {
	if (open($fh, "<", "CVS/Entries")) {
		system("cvs -f add ChangeLog") unless (scalar grep /^\/ChangeLog\//, <$fh>);
		close($fh);
	}
} elsif ($vcs eq "svn") {
	if (open($fh, "<", ".svn/entries")) {
		system("svn add ChangeLog") unless (scalar grep /ChangeLog/, <$fh>);
		close($fh);
	}
} else {
	system("$vcs{$vcs}{add} ChangeLog 2>&1 > /dev/null");
}

# vim: set ts=4 sw=4 tw=0:
